~~bzoj早就老了~~如今最受歡迎的OJ固然是你谷了,一些比較經典的題目仍是很不錯的。ios
LINK:想了好幾個月的dp 翻硬幣 我想到一個狀態 f[i][j]表示翻了i次且當前局面爲j的方案數最後輸出f[n][目標方案便可]。c++
可是這裏面存在着一些問題 好像這個轉移都是一個組合數級別的因此不可能轉移出來的 。忽然有一個想法設f[i][j]表示通過i次翻轉有j個位置是合法的方案數這樣就能夠轉移了。數組
n^2遞推組合數便可。ide
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const ll MAXN=110; ll n,m,k,cnt; ll c[MAXN][MAXN]; ll f[MAXN][MAXN];//f[i][j]表示前i次翻轉有j個位置合法的方案數。 char a[MAXN],b[MAXN]; inline void get_combination() { for(ll i=0;i<=n;++i)c[i][0]=1; for(ll i=1;i<=n;++i)c[i][1]=i; for(ll i=1;i<=n;++i) for(ll j=1;j<=i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } signed main() { //freopen("1.in","r",stdin); n=read();k=read();m=read(); scanf("%s",a+1); scanf("%s",b+1); for(ll i=1;i<=n;++i) if(a[i]==b[i])++cnt; get_combination(); f[0][cnt]=1; for(ll i=1;i<=k;++i) { for(ll l=0;l<=n;++l)//枚舉上一次狀態 { if(!f[i-1][l])continue; for(ll j=0;j<=n;++j)//枚舉當前狀態 { if(abs(l-j)>m)continue; ll s1=abs(l-j); if((m-s1)&1)continue; ll s2=(m-s1)>>1; ll rest=n-l; if(j>=l) { if(s1+s2<=rest)f[i][j]=(f[i][j]+((f[i-1][l]*c[rest][s1+s2]%mod)*c[l][s2])%mod)%mod; } else if(s1+s2<=l)f[i][j]=(f[i][j]+((f[i-1][l]*c[rest][s2]%mod)*c[l][s1+s2])%mod)%mod; } } } printf("%lld\n",f[k][n]); return 0; }
LINK:系統設計想了2h都沒思路的題目XR-round系列 才知道有這種題目的存在,好難想啊。學習
當時比賽的時候都沒能想出來 一棵有根樹每次指定一個節點開始沿着m這個序列向下跑問中止時到達哪一個節點了?顯然的nQ暴力,除此以外也就沒有什麼了。優化
可是呢$n,Q<=200000$這顯然是不能過的,考慮一下正解?離線?由於還帶修改因此基本上須要在線搞。ui
觀察 修改的是什麼m這個序列 咱們從題目自己下手 修改這個序列是爲了阻止咱們暴力的幹某件事情因此進行了修改。其實就算不修改咱們離線以後儘管每一個點都便利了一次可是最後的複雜度仍免不了是MQ的照樣歇菜。spa
如何對於一棵樹的某一條鏈和這個序列的l r進行快速匹配呢?咱們發現了咱們先對樹的每一層進行標號 而後設根到某個節點x的路徑上的字符串爲sx那麼發現了什麼這個字符串最多有O(n)。設計
因此對於一次詢問咱們快速提取出l到r這個區間內的字符串再加上詢問的從x出發的那個節點sx且若是存在咱們就獲得了目標節點的字符串信息了直接查找。3d
查找的話仍是字符串hash比較好一點這樣若是保證存在咱們直接在主席樹上找是否有這個值出現便可。可是若是不存在怎麼辦?這是一個比較嚴重的問題好像很難解決的樣子,不過好像也是能夠的考慮到答案必然是某個節點咱們不妨二分一下深度 答案之上的節點咱們必然也能夠找到 因此顯然二分是成立的。複雜度$Q(logn)^2$可是注意到一個問題帶修改的m序列hash值也在變暴力一點直接線段樹維護hash值結束..
可是太暴力了,寫兩棵樹我不是很想寫,看了題解 發現果真和題解差的好遠,題解直接用線段樹維護了hash值,而後查找直接線段樹上二分省去了我那個二分的log而後斷定的話直接hash表中進行斷定。
真的是簡潔,那麼這道題就這樣作便可。可是通過不懈的對拍發現了天然溢出hash發生了衝突 在這裏咱們很難使用掛鏈法由於咱們就是想要儘量的沒有衝突。
採用雙hash雙map...(固然可能會很慢.. 仍是被卡了 單模數hashwa了?我忍了 雙模數hash也wa了?我也忍了..不寫了回來請教一下舉辦這場比賽的人再說.
因而我開大了進制數 書上說 131 13331 這兩個進制數衝突率極低 可是這兩個我都試了一直wa 改模數也是wa 開大到1000000007 發現就不wa了。很開心。
可是T了... 常數實在是太大了 線段樹上二分 + map 兩個常數極大的東西 開o2也無論用。
終於被我找到了方法 手動hash 關掉map .. 由於樹狀數組上倍增對於這道題來講實在是太難寫了。我真不會quq
因此 開了拉鍊法 注:1000003 是一個質數 而1000007 不是質數。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 #define ull unsigned long long #define un unsigned #define P 1000000007ull #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum #define ans(p) t[p].ans using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=500010,Mod=1000003; int n,m,Q,root,rt,id,len; ull f[MAXN],p[MAXN],ans,c,ver[Mod],ver1[Mod]; int a[MAXN],lin[Mod],nex[Mod]; vector<int>g[MAXN]; inline void add(int x,int y,ull z) { ver[++len]=y; ver1[len]=z; nex[len]=lin[x]; lin[x]=len; } inline void insert(ull x,int y) { int w=x%Mod; add(w,y,x); } inline int find(ull x) { int w=x%Mod; for(int i=lin[w];i;i=nex[i]) if(ver1[i]==x)return ver[i]; return 0; } struct wy { int l,r; ull sum; int ans; }t[MAXN<<2]; inline void dfs(int x) { int cnt=0; sort(g[x].begin(),g[x].end()); for(un int i=0;i<g[x].size();++i) { int tn=g[x][i]; ++cnt; f[tn]=f[x]*P+cnt; insert(f[tn],tn); dfs(tn); } } inline void pushup(int w){sum(w)=sum(l(w))*p[ans(r(w))]+sum(r(w));} inline void build(int &p,int l,int r) { if(!p)p=++id; t[p].ans=(r-l+1); if(l==r){sum(p)=a[l];return;} int mid=(l+r)>>1; build(l(p),l,mid); build(r(p),mid+1,r); pushup(p); } inline int ask(int s,int l,int r,int x) { if(l==r) { c=ans*p[ans(s)]+sum(s); if(find(c)) { ans=c; return r; } return l-1; } int mid=(l+r)>>1; if(l>=x) { c=ans*p[ans(s)]+sum(s); if(find(c)) { ans=c; return r; } c=ans*p[ans(l(s))]+sum(l(s)); if(find(c)) { ans=c; return ask(r(s),mid+1,r,x); } return ask(l(s),l,mid,x); } if(x>mid)return ask(r(s),mid+1,r,x); int w=ask(l(s),l,mid,x); if(w==mid) { c=ans*p[ans(r(s))]+sum(r(s)); if(find(c)) { ans=c; return r; } return ask(r(s),mid+1,r,x); } return w; } inline void query(int s,int l,int r,int L,int R) { if(L>R)return; if(L<=l&&R>=r) { ans=ans*p[ans(s)]+sum(s); //cout<<s<<endl; return; } int mid=(l+r)>>1; if(L<=mid)query(l(s),l,mid,L,R); if(R>mid)query(r(s),mid+1,r,L,R); return; } inline void modify(int p,int l,int r,int x,int w) { if(l==r){sum(p)=w;return;} int mid=(l+r)>>1; if(x<=mid)modify(l(p),l,mid,x,w); if(x>mid)modify(r(p),mid+1,r,x,w); pushup(p); } int main() { //freopen("1.in","r",stdin); n=read();m=read();Q=read();p[0]=1; for(int i=1;i<=n;++i) { int x=read(); p[i]=p[i-1]*P; if(!x)root=i; else g[x].push_back(i); } dfs(root);insert(0,root); //for(int i=1;i<=n;++i)cout<<f[i]<<endl; for(int i=1;i<=m;++i)a[i]=read(); build(rt,1,m); //ans=0;query(rt,1,n,1,2); //cout<<ans<<endl; for(int i=1;i<=Q;++i) { int op,l,r,x; op=read(); if(op==1) { x=read();l=read();r=read(); ans=f[x]; int w=ask(rt,1,m,l); w=min(w,r); ans=f[x]; query(rt,1,m,l,w); printf("%d\n",find(ans)); } else { int y; x=read();y=read(); modify(rt,1,m,x,y); } } return 0; }
很開心。
LINK:古代豬文 這題好啊 SDOI 2010 三道豬的神題..果真強大。一句話題意 求 $G^{\sum_{d|n}{C(d,n)}}mod999911659$
其中C是組合數 設p=999911659 當G是p的倍數顯然爲0 考慮不是p的倍數的時候咱們顯然能夠發現p是個質數 那麼直接歐拉降冪Y,
降冪以後至關於指數%上p-1 如今求一個組合數%p-1便可。這個d咱們能夠根號n枚舉 在能夠承受的範圍內。關鍵是如何求一個組合數模p-1.
p-1顯然不是一個質數 不能盧卡斯定理 可是 咱們 能夠發現一個問題 p-1=2*3*166651943 暴力枚舉166651943 發現能整除的有4679 35617 再次暴力枚舉4679 和35617 發現這兩個都是質數
也就是說p-1分解成了4個質數且這四個質數的次數都爲1 那麼 咱們顯然能夠利用組合數模這個質數的答案而後,發現了什麼其實咱們答案%p-1很難搞可是答案%這四個質數答案很好搞。
也就是有了4個同餘方程 且合併完以後恰好是咱們的p-1 那麼同餘方程一解 快速冪便可。
碼了一個h 寫寫停停仍是對數論的一些定理不是太熟悉..
#//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned using namespace std; char *ft,*fs,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll p=999911659; ll G,n,ans[5],an,xx,yy; ll jc[5][400000],inv[5][400000]; ll y[5]={0,2,3,4679,35617}; inline ll gcd(ll a,ll b) { if(!b)return a; return gcd(b,a%b); } inline ll Lucas(ll a,ll b,ll mod,ll w) { if(a<b)return 0; if(a==b)return 1; if(b==0)return 1; if(a<mod)return ((jc[w][a]*inv[w][b])%mod*inv[w][a-b])%mod; return (Lucas(a%mod,b%mod,mod,w)*Lucas(a/mod,b/mod,mod,w))%mod; } inline ll ksm(ll b,ll pp,ll mod) { ll ans=1; while(pp) { if(pp&1)ans=ans*b%mod; b=b*b%mod; pp=pp>>1; } return ans; } inline void get_ans(ll x)//求C(n,x) { for(ll i=1;i<=4;++i) ans[i]+=Lucas(n,x,y[i],i); } inline void exgcd(ll a,ll b) { if(!b){xx=1,yy=0;return;} exgcd(b,a%b); ll z=xx;xx=yy;yy=z-a/b*yy; } inline void merge()//同餘方程求答案 { for(ll i=1;i<=4;++i)//顯然通解是x=Mi*Ti*ai { ll M=(p-1)/y[i];//求解 Ti Mi*Ti=ai(mod y[i]) exgcd(M,y[i]); xx=(xx%y[i]+y[i])%y[i]; an=(an+(M*xx)%(p-1)*ans[i])%(p-1); } } inline void solve()//求G的次數ans { for(ll i=1;i*i<=n;++i) if(n%i==0) { get_ans(i); if(i!=n/i)get_ans(n/i); } merge(); return; } signed main() { //freopen("1.in","r",stdin); n=read();G=read(); if(gcd(G,p)==p){puts("0");return 0;} for(ll i=1;i<=4;++i) { jc[i][0]=1; for(ll j=1;j<y[i];++j) jc[i][j]=jc[i][j-1]*j%y[i]; inv[i][y[i]-1]=ksm(jc[i][y[i]-1],y[i]-2,y[i]); for(ll j=y[i]-2;j>=1;--j) inv[i][j]=inv[i][j+1]*(j+1)%y[i]; } solve(); printf("%d\n",ksm(G,an,p)); return 0; }
LINK:最短路的變形問題 其實就是隨機刪掉一條邊在最差的狀況下1 到 n的最短路 顯然咱們任意求出一條最短路 而後若是刪掉的不是最短路上的邊那麼答案不變。
考慮 枚舉刪掉最短路的邊 而後再跑最短路 每次取max便可。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<cctype> #include<algorithm> #include<cstdlib> #include<queue> #include<stack> #include<set> #include<bitset> #include<vector> #include<map> #include<deque> #include<utility> #define INF 1000000000 #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define mp make_pair #define S second using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010,maxn=1000010; int n,m,len=1,top,mark,ans; int pre[MAXN],s[MAXN],dis[MAXN],vis[MAXN]; int lin[MAXN],ver[maxn<<1],nex[maxn<<1],e[maxn<<1]; priority_queue<pair<int,int> > q; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void dij() { for(int i=1;i<=n;++i)vis[i]=0,dis[i]=INF; dis[1]=0;q.push(mp(0,1)); while(q.size()) { int x=q.top().S;q.pop(); if(vis[x])continue; vis[x]=1; for(int i=lin[x];i;i=nex[i]) { if(i==mark||((i^1)==mark))continue; int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; pre[tn]=i; q.push(mp(-dis[tn],tn)); } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } dij(); int w=n; while(w!=1) { s[++top]=pre[w]; w=ver[pre[w]^1]; } for(int i=1;i<=top;++i) { mark=s[i]; dij(); ans=max(ans,dis[n]); } printf("%d\n",ans); return 0; }
可是複雜度不夠優秀 n^2logn 我以爲不太好...考慮更優秀的作法。
仍是上一道高難度的題目比較有說服力:LINK:道路堵塞 每次都去掉一條邊 而後詢問 最短路是多長。
n<100000 m<200000 這個範圍 咱們標記最短路若是詢問不是最短路上的邊咱們就不跑是的話咱們就再跑一邊這樣的複雜度顯然仍是上述的複雜度。。
考慮優化..本人不會先咕了。
我確定 咕不超過兩個月這個問題激怒了 我 學習了一種比較假的作法(畢竟spfa已經死了 在升級以前也不可能復活了。
具體 作法是這樣的 題目中已經給出了最短路 首先暴力顯然是 暴力刪邊 而後暴力跑。複雜度太高。
考慮動態的spfa 什麼也就是咱們只spfa一次 求出全部的 答案。雖然複雜度最壞nm 可是 顯然 在一些狀況下出題人仍是會讓spfa 復生的。
首先把全部的邊都ban掉 求出 $g_i$ 表示最短路上的第i個點到終點的最短路。 先從1跑一邊spfa但 最短路上的邊都不能用。
而後 一旦跑到最短路上的某一點 那麼 就多是答案 $dis_i+g_i$ 這樣的話就成爲了答案的候選項 加入堆中。
考慮 不斷啓用 最短路上邊 可是同時也不能用當前的最短路 也就是當前的最短路以前的點都是不合法的因此pop 。
總複雜度 1次spfa的操做 nt 吧 /cy
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<int,int> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=200010; int n,m,len,L,t,h; int pos[MAXN],dis[MAXN],q[MAXN<<4]; int w[MAXN],vis[MAXN],g[MAXN],id[MAXN],mark[MAXN]; int lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN]; priority_queue<pii> s; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void spfa(int xx) { t=h=0; q[++t]=xx;vis[xx]=1; while(h++<t) { int x=q[h];vis[x]=0; for(int i=lin[x];i;i=nex[i]) { if(mark[i])continue; int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; if(id[tn])s.push(mk(-(dis[tn]+g[id[tn]]),id[tn])); else if(!vis[tn])q[++t]=vis[tn]=tn; } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read();L=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); add(x,y,read()); } for(int i=1;i<=L;++i) { w[i]=read(); mark[w[i]]=1; pos[i+1]=ver[w[i]]; id[ver[w[i]]]=i+1; } memset(dis,0x3f,sizeof(dis)); id[1]=pos[1]=1; for(int i=L;i>=1;--i)g[i]=g[i+1]+e[w[i]]; dis[1]=0;spfa(1); for(int i=1;i<=L;++i) { while(s.size()&&s.top().second<=i)s.pop(); if(!s.size())puts("-1"); else printf("%d\n",-s.top().first); dis[pos[i+1]]=dis[pos[i]]+e[w[i]]; spfa(pos[i+1]); } return 0; }
清醒了一下思考了 一下 個人拓撲的思路是錯誤的 一條邊隨機的不能走,而不是一個最優解被捨棄,這樣作法是不正確的 由於存在剩下的解不是最差解中的最優解。
考慮dp f[i][j]表示到了第i個點此時通過k次狀況造成的最優解 仔細思考咱們不知道後續的狀態可能這個狀態沒法達到最差的狀態,因此還須要更改。
不難發現 想要轉移須要後續狀態的加持 再設一個更清晰的狀態 f[i][j] 表示到達第i個點此時還有j次不可控的狀況未發生且到達終點的最優解這個顯然是min max 轉移我想。
顯然有終點狀態 f[n][0]=0;在一張DAG上直接搜便可。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 20000000000000000ll #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define d(i) t[i].d #define x(i) t[i].x #define db double using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll MAXN=150010; ll n,m,k,t,h,len; ll lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN]; ll f[MAXN][15],q[MAXN],tmp[MAXN],ru[MAXN]; inline void add(ll x,ll y,ll z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline int dfs(int x,int s) { if(f[x][s]>=0)return f[x][s]; ll minn=INF,maxx=0; if(s>=1) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; dfs(tn,s-1); minn=min(minn,f[tn][s-1]+e[i]); } } for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; dfs(tn,s); maxx=max(maxx,f[tn][s]+e[i]); } f[x][s]=min(minn,maxx); return f[x][s]; } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); memset(f,0xcf,sizeof(f)); for(ll i=1;i<=m;++i) { ll x,y,z; x=read();y=read();z=read(); add(x,y,z); } f[n][0]=0; dfs(1,k); printf("%lld\n",f[1][k]); return 0; }
注意轉移 max 套min 便可。
對於答案顯然是二分 可是並很差寫 由於check的緣由 只有一堆信號塔 以及最左端和最右端 將其都話點最後判斷 最左端和最右端是否相連,這個顯然是很差搞得仍是存在 最左端出現多個表明位置。
不妨維護某個集合的最大向左延伸 和最大向右延伸便可。而後n^2並查集 這個排序可能作不到O(n)可能存在兩個點的橫座標相同因此O(n)不太好作因此就採用暴力枚舉了。
因此此題 想仍是很好想的作法須要斟酌一番。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 20000000000000000ll #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010; int n,m; struct wy { int x,y; }t[MAXN]; int f[MAXN]; db L[MAXN],R[MAXN]; inline db dis(int x,int y) { return sqrt(1.0*(t[x].x-t[y].x)*(t[x].x-t[y].x)+(ll)(t[x].y-t[y].y)*(ll)(t[x].y-t[y].y)); } inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);} inline int check(db x) { for(int i=1;i<=m;++i) { L[i]=t[i].x-x; R[i]=t[i].x+x; f[i]=i; //求最長向左延伸和最長向右延伸 } for(int i=1;i<=m;++i) for(int j=i+1;j<=m;++j) { db w=dis(i,j); if(w<=x+x) { int xx=getfather(i); int yy=getfather(j); if(xx==yy)continue; f[xx]=yy; L[yy]=min(L[yy],L[xx]); R[yy]=max(R[yy],R[xx]); } } for(int i=1;i<=m;++i) { int xx=getfather(i); if(L[xx]<=0&&R[xx]>=n)return 1; } return 0; } int main() { freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); t[i]=(wy){x,y}; } db l=0,r=n; while(l+EPS<r) { db mid=(l+r)/2; if(check(mid))r=mid; else l=mid; } printf("%.2lf\n",l); return 0; }
題目是很簡單 操做 將一個區間內全部比Hi小的數改成Hi 區間很大 考慮線段樹 但是離散以後貌似也沒有什麼更好的作法,考慮離散化以後暴力的搞最後統一下傳懶標記可是存在一個問題咱們把區間縮掉了,統計答案的時候咱們不知道當前某兩個點之間的的值究竟是0仍是存在對答案是有貢獻的這顯然很難維護這個東西 因此考慮動態開點。
可是我指望的作法是標記永久花 區間這麼大可是一些區間是沒必要要的考慮 動態開點 但是這樣加入懶標記以後不能pushdown 由於一旦pushdown就會下傳好多的節點 新建好多節點致使時間和空間雙雙爆炸。
考慮標記永久化 最後 便利一下整顆樹來統計答案。固然這個我也不太熟悉 本身隨便寫一個好了。
再三斟酌仍是本身寫(找不到之前的標記永久化代碼了頭疼...烏拉隨便寫的居然過了 雖然數組開小了答案 算的有點鍋。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define l(x) t[x].l #define add(x) t[x].add #define r(x) t[x].r using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=40010; int n,maxx=1000000000; int root,id; ll ans; struct wy { int l,r; int add; }t[MAXN*30<<1]; inline void change(int &p,int l,int r,int L,int R,int x) { if(!p)p=++id; if(L==l&&R==r) { add(p)=max(add(p),x); return; } int mid=(l+r)>>1; if(L>mid)change(r(p),mid+1,r,L,R,x); else { if(R<=mid)change(l(p),l,mid,L,R,x); else change(l(p),l,mid,L,mid,x),change(r(p),mid+1,r,mid+1,R,x); } } inline void ask(int p,int l,int r,ll mx)//mx表示當前區間的最大值 { mx=mx>add(p)?mx:add(p); if(l==r){ans+=mx;return;} if(!p) { ans+=(r-l+1)*mx; return; } int mid=(l+r)>>1; ask(l(p),l,mid,mx); ask(r(p),mid+1,r,mx); return; } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) { int x,y,z; x=read();y=read()-1;z=read(); change(root,1,maxx,x,y,z); } ask(root,1,maxx,0); printf("%lld\n",ans); return 0; }
對於這個代碼的複雜度咱們顯然能夠看出 區間修改時複雜度爲logn 又把區間分紅logn段 總共有nlogn段最後統計一遍樹只會統計到有值得點因此複雜度仍爲nlogn.
LINK:WQS二分模板題 早就想寫了。
剛好有need 條邊的生成樹而且使這顆生成樹的權值和最小 因此考慮 dp 這玩意最小生成樹 不太行了就咱們從dp的方面想辦法。
這裏咱們先無論答案怎麼求設 fix 表示在前i條邊中強制選擇了x條邊的的最小价值。 這 畫一張圖 發現這造成了一個凸包 考慮如何求出最優解 一個比較靠譜的作法是 用一條線來切這個凸包 關於凸包上
本人仍是不會 因此 仍是咕了。
LINK:同類分佈 一道神仙 數位dp 好題啊 我根本就不會作QuQ
考慮暴力 直接爆搜 考慮dp 咱們發現模數很煩 由於咱們只有分配數字到最後才能知道模數是多少 不妨咱們直接暴力一點枚舉模數。
而後對於 就能夠直接設計狀態 $f_{i,j,k}$ 表示到了第i位 此時 餘數是j 和位 k的方案數。
這樣就能夠dp下去了 複雜度 大概爲 1e8左右 不過值得注意的是 mark標記的維護。即最高位的限制。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<ll,ll> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=9*20+1,maxn=20; ll f[maxn][MAXN][MAXN];//f[i][j][k]表示到了第i位 此時餘數是j 和爲k的方案數 ll n,m,ans,cnt,top,mod; ll b[maxn]; inline ll dfs(ll pos,ll res,ll sum,ll mark)//mark 表示是否 有最高位的限制 { if(!mark&&f[pos][res][sum]!=-1)return f[pos][res][sum]; if(!pos) { if(!res&&sum==mod)f[pos][res][sum]=1; else f[pos][res][sum]=0; return f[pos][res][sum]; } ll ans=0,maxx=mark?b[pos]:9; for(int i=0;i<=maxx;++i) { if(i==maxx)ans+=dfs(pos-1,(res*10+i)%mod,sum+i,mark&1); else ans+=dfs(pos-1,(res*10+i)%mod,sum+i,0); } if(!mark)f[pos][res][sum]=ans; return ans; } inline ll slove(ll x) { top=0;ll cnt=0; while(x) { b[++top]=x%10; x=x/10; } for(int i=1;i<=top*9;++i) { memset(f,-1,sizeof(f)); mod=i; cnt=cnt+dfs(top,0,0,1); } return cnt; } signed main() { //freopen("1.in","r",stdin); m=read();n=read(); ans=slove(n); cnt=slove(m-1); printf("%lld\n",ans-cnt); return 0; }
LINK:末日的傳說 想了幾個月終於以爲本身能夠寫這道題了。 ~~這多是我咕的最久的題目了~~
早就有了一個初步的想法了 可是後續的細節最近纔想出來 我是作了旅行的增強版纔對字典序理解的更加深入 固然 旅行那道題本身思考出來以後深入理解了什麼叫作字典序。
當前能取最優就不要讓當前較優後面更優 換句話說 當前只須要取到最優的決策便可 和後面的決策無關。
因此 考慮構造一個序列字典序最小且逆序對數有m個 咱們能夠一位一位考慮這個作法 第一位能取1麼 這得看後面的數字組成的逆序對數可否大於m了。
若是大於m的話當前顯然 能夠取到1 不行的話那麼1必然要放到當前這個位置以後考慮接下來放的數字必然大於1 那麼此時必然造成了一個逆序對 --m便可 一直尋找到這樣的數字。不斷作下去便可。
能夠發現複雜度是n^2的固然過不了這道題 考慮加快尋找這個過程 顯然主席樹上二分便可 nlogn 不過代碼複雜度太高 考慮樹狀數組+二分 因此以n(logn)^2的複雜度便可經過本題。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<ll,ll> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=50010; int n,m; int vis[MAXN]; int sum[MAXN],ans[MAXN]; int c[MAXN]; inline void add(int x,int y) { while(x<=n) { c[x]+=y; x+=x&(-x); } } inline int ask(int x) { int cnt=0; while(x) { cnt+=c[x]; x-=x&(-x); } return cnt; } inline int calc(int x) { int l=1,r=n; while(l+1<r) { int mid=(l+r)>>1; if(ask(mid)>=x)r=mid; else l=mid; } if(ask(l)>=x)return l; return r; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i)sum[i]=(ll)i*(i-1)/2,add(i,1); for(int i=1;i<=n;++i) { int res=n-i;//除了當前這個數字以後還有多少位 int cnt=1,ww; ww=max(0,m-sum[res]); cnt+=ww; int w=calc(cnt); ans[i]=w; add(w,-1); m=m-ww; } for(int i=1;i<=n;++i)printf("%d ",ans[i]); return 0; }
固然 上述作法還不夠優秀 顯然還有更優的作法 仍是考慮上述的過程 1放不到這個位置以後咱們仍是想讓當前數字取到最優 那麼想要取到最優那麼m就應該儘可能小 因此1放到最後一個位置m減少的幅度會很大 因此根據貪心的來選直接把1放到最後考慮下一個數字便可。
思考一下貪心的原則 能夠發現這樣是正確的 固然也有一個比較嚴謹的證實 能夠自行腦補。複雜度O(n)
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<ll,ll> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=50010; int n,m,st,en; int vis[MAXN]; int sum[MAXN],ans[MAXN]; int main() { //freopen("1.in","r",stdin); n=read();m=read();en=n+1; for(int i=1;i<=n;++i)sum[i]=(ll)i*(i-1)/2; for(int i=1;i<=n;++i) { int res=n-i;//除了當前這個數字以後還有多少位 if(sum[res]>=m)ans[++st]=i; else m-=res,ans[--en]=i; } for(int i=1;i<=n;++i)printf("%d ",ans[i]); return 0; }
LINK:區間第k小
1 直接sort 2 歸併樹 3 主席樹。 第一種 nmlogn 無腦的東西 無聊 第二種 利用歸併排序+二分+lower_bound無聊的方法 mlogn*logn*logn 這種作法雖然較劣 可是仍是 歸併樹的思想仍是要學習的。
3 主席樹 比較經典的作法再也不贅述。代碼是 主席樹 可是歸併樹這個東西真的不多見。可能會用到的東西。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n,m,id,minn=INF,maxx=-INF; int a[MAXN],root[MAXN]; struct wy { int l,r; int sum; }t[MAXN*30<<1]; inline void insert(int &p,int last,int l,int r,int x) { p=++id;t[p]=t[last]; if(l==r){++sum(p);return;} int mid=(l+r)>>1; if(x<=mid)insert(l(p),l(last),l,mid,x); else insert(r(p),r(last),mid+1,r,x); sum(p)=sum(l(p))+sum(r(p)); } inline int ask(int p,int last,int l,int r,int k) { if(l==r)return l; int mid=(l+r)>>1; int v=sum(l(p))-sum(l(last)); if(v>=k)return ask(l(p),l(last),l,mid,k); return ask(r(p),r(last),mid+1,r,k-v); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i) { a[i]=read(); minn=min(minn,a[i]); maxx=max(maxx,a[i]); } for(int i=1;i<=n;++i)insert(root[i],root[i-1],minn,maxx,a[i]); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); printf("%d\n",ask(root[y],root[x-1],minn,maxx,z)); } return 0; }
LINK:應該是二分的題目 想了一下以爲挺有意思的 跟之前見過的模型差很少 總結一下。
一個序列中 求出一段連續子序列使其 平均數最大 ,有點腦殘的問題 顯然其中最大的那個數獨自成爲一個子序列就會使 答案最優 考慮 一個限制長度須要大於k 這就有一點點複雜了...
顯然 遇到這種問題不防把式子寫出來 $max{\frac{\sum_{a_i}}{w}}$ 考慮如何求出答案 顯然是分數規劃問題 不妨二分一下答案。考慮check。
在答案是mid時可能不存在這樣的序列!但此時考慮若是存在最優解ans ans的長度必定是>=k的 因此 顯然必然存在一個長度大於k的序列其累加和大於0 因此對於每個右端點尋找到一個最小的左端點便可。這樣顯然能夠check答案。因此就解決了問題。我找到了題目的連接!
LINK:尋找段落 code:
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n; int S,T; int a[MAXN]; int minn=INF,maxx=-INF; db b[MAXN]; int q[MAXN],h,t; inline int check(db x) { db v=-INF; h=1;t=0; for(int i=1;i<S;++i)b[i]=a[i]-x+b[i-1]; q[++t]=0; for(int i=S;i<=n;++i) { b[i]=a[i]-x+b[i-1]; while(h<=t&&q[h]<i-T)++h; v=max(v,b[i]-b[q[h]]); while(h<=t&&b[q[t]]>=b[i-S+1])--t; q[++t]=i-S+1; } if(v<0)return 0; return 1; } int main() { //freopen("1.in","r",stdin); n=read();S=read();T=read(); for(int i=1;i<=n;++i) { a[i]=read(); maxx=max(maxx,a[i]); minn=min(minn,a[i]); } db l=minn,r=maxx; while(l+EPS<r) { db mid=(l+r)/2;; if(check(mid))l=mid; else r=mid; } printf("%.3lf",l); return 0; }
這道題目 想要讓咱們去掉中間的連續子序列 使得剩下的平均奶產量最小,貪心顯然沒法解決這個問題。
仍是二分 可是 考慮一個問題上面兩道題目是否等價的問題 一個兩邊平均值最小 一箇中間平均值最大 遺憾的是隨便出個數據便可說明顯然是不等價的問題。
那麼 我苦思冥想一中午 發現 這不是裸的二分麼 我sb了...
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n; int a[MAXN]; int minn=INF,maxx=-INF; db b[MAXN]; int q[MAXN],h,t; inline int check(db x) { db v=INF; h=1;t=0; for(int i=n;i>=1;--i)b[i]=a[i]-x+b[i+1]; for(int i=3;i<=n;++i) { while(h<=t&&b[q[t]]>=b[i])--t; q[++t]=i; } db cnt=0; for(int i=1;i<=n-2;++i) { cnt+=a[i]-x; while(h<=t&&q[h]<=i+1)++h; v=min(v,cnt+b[q[h]]); } if(v>0)return 0; return 1; } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) { a[i]=read(); maxx=max(maxx,a[i]); minn=min(minn,a[i]); } db l=minn,r=maxx; while(l+EPS<r) { db mid=(l+r)/2;; if(check(mid))r=mid; else l=mid; } printf("%.3lf",l); return 0; }
LINK:最大正方形增強版 每次都ban掉一個點求最大的正方形 前一次ban 對後一次有影響的那種。
怎麼作 首先求最大的正方形 有兩種方法 懸線法 或者直接 dp 惋惜 這兩種方法都不支持動態的在圖上更改 一旦更改就是n^2的 複雜度 n^2k 這顯然不是咱們指望承受的複雜度。
考慮 怎麼解決這個問題。 dp一旦修改死死的n^2 考慮懸線法求答案 動態ban點考慮動態加點ban點對答案影響太大 一旦ban掉一個答案 還得從新找一個答案很難操做 考慮倒序 加點。
而後 更新一下向左延伸和向右延伸的最大長度 求出以當前這個點爲基準的最大正方形看可否更新答案便可整個複雜度爲nk+n^2
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 #define ull unsigned long long #define un unsigned #define P 1000000007ull using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=2010; int n,m,k,cnt; char a[MAXN][MAXN]; struct wy{int x,y;}t[MAXN]; int l[MAXN][MAXN],r[MAXN][MAXN]; int f[MAXN][MAXN],ans[MAXN]; int L,R,q[MAXN],w[MAXN]; inline int check(int d,int y) { L=1;R=0; for(int i=1;i<=n;++i) { while(L<=R&&l[q[R]][y]>=l[i][y])--R; q[++R]=i; while(L<=R&&q[L]<=i-d)++L; w[i]=l[q[L]][y]; } L=1;R=0; for(int i=1;i<=n;++i) { while(L<=R&&r[q[R]][y]>=r[i][y])--R; q[++R]=i; while(L<=R&&q[L]<=i-d)++L; w[i]+=r[q[L]][y]-1; } for(int i=d;i<=n;++i)if(w[i]>=d)return 1; return 0; } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=n;++i)scanf("%s",a[i]+1); for(int i=1;i<=k;++i) { int x,y; x=read();y=read(); t[i]=(wy){x,y}; a[x][y]='X'; } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(a[i][j]=='X') { l[i][j]=0;f[i][j]=0; continue; } l[i][j]=l[i][j-1]+1; f[i][j]=min(f[i][j-1],min(f[i-1][j-1],f[i-1][j]))+1; cnt=max(cnt,f[i][j]); } for(int j=m;j;--j) { if(a[i][j]=='X')r[i][j]=0; else r[i][j]=r[i][j+1]+1; } } for(int i=k-1;i;--i) { ans[i+1]=cnt; int x=t[i+1].x; int y=t[i+1].y; a[x][y]='.'; for(int j=1;j<=m;++j)l[x][j]=a[x][j]=='X'?0:l[x][j-1]+1; for(int j=m;j;--j)r[x][j]=a[x][j]=='X'?0:r[x][j+1]+1; while(check(cnt+1,y))++cnt; } ans[1]=cnt; for(int i=1;i<=k;++i)printf("%d\n",ans[i]); return 0; }
LINK:統計距離問題很是巧妙的題目。
初看這道題以爲很難作只能枚舉座標而後 前綴和什麼的顯然過不了 。考慮轉換 畫在圖上一個點的可控範圍是一個相似菱形的正方形 這種很難搞吧 咱們掃描線維護不了這麼不規則的東西。
考慮切換成正方形! 怎麼切換?發現轉契比雪夫距離就變成了一個正方形的問題了 神奇 。而後呢咱們就是用一個邊長爲2k的正方形來 圈一些點 使其代價最大。
(窗口的星星可還行 而後就是把每一個點當成 一個正方形掃描線掃一下便可。
注意離散化 要不就平移座標 可是動態開點標記永久化好像不太行的樣子維護區間最值我也不太會。由於平移座標暴力線段樹了 因此空間消耗大會T。
最後仍是離散了一下。
//單個局面偶數必敗 //以爲單個局面除了1 剩下也是必敗 直接異或好了 //#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum #define tag(p) t[p].tag #define zz p<<1 #define yy p<<1|1 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010<<1; int n,k,cnt,top,ans; struct wy { int l,r; int tag; int sum; }t[MAXN<<2],w[MAXN]; int b[MAXN]; inline int cmp(wy a,wy b){return a.l<b.l;} inline void build(int p,int l,int r) { l(p)=l;r(p)=r; if(l==r)return; int mid=(l+r)>>1; build(zz,l,mid); build(yy,mid+1,r); } inline void pushdown(int p) { sum(zz)+=tag(p); sum(yy)+=tag(p); tag(zz)+=tag(p); tag(yy)+=tag(p); tag(p)=0; return; } inline void change(int p,int l,int r,int x) { if(l<=l(p)&&r>=r(p)) { sum(p)+=x; tag(p)+=x; return; } int mid=(l(p)+r(p))>>1; if(tag(p))pushdown(p); if(l<=mid)change(zz,l,r,x); if(r>mid)change(yy,l,r,x); sum(p)=max(sum(zz),sum(yy)); } inline void discrete() { sort(b+1,b+1+cnt); for(ll i=1;i<=cnt;++i)if(i==1||b[i]!=b[i-1])b[++top]=b[i]; for(ll i=1;i<=cnt;++i) { w[i].r=lower_bound(b+1,b+1+top,w[i].r)-b; w[i].tag=lower_bound(b+1,b+1+top,w[i].tag)-b; } } int main() { freopen("1.in","r",stdin); n=read();k=read()<<1; for(int i=1;i<=n;++i) { int x,y,z; z=read();x=read();y=read(); w[++cnt].l=x+y;b[cnt]=w[cnt].r=x-y; w[cnt].tag=x-y+k;w[cnt].sum=z; w[++cnt].l=x+y+k+1;w[cnt].r=x-y; w[cnt].tag=x-y+k;w[cnt].sum=-z; b[cnt]=x-y+k; } discrete(); sort(w+1,w+1+cnt,cmp); build(1,1,top); for(int i=1;i<=cnt;++i) { change(1,w[i].r,w[i].tag,w[i].sum); //cout<<w[i].l<<endl; //cout<<w[i].sum<<' '<<sum(1)<<endl; while(w[i].l==w[i+1].l&&i+1<=cnt) { ++i; change(1,w[i].r,w[i].tag,w[i].sum); //cout<<w[i].sum<<' '<<sum(1)<<' '<<endl; } ans=max(ans,sum(1)); } printf("%d\n",ans); return 0; }