來寫題解啦QwQ
原本想上紅的,結果沒作出D。。。。前端
CF1137Aios
給定一個\(n*m\)的網格,每一個格子裏都有一個數,對於任意一行和任意一列,要求把這\(n+m-1\)個數從新用正整數編號,而且對於這一行,數與數之間的大小關係不變,對於這一列同理。求出任意一行和任意一列編號使用的最大編號的最小值。數組
讀題讀半天。。。
看懂了題目就不難了。
對於每一行和每一列先分別離散,記錄每一個位置在離散後的值,
而後合併以後取較大的那個,而後剩下的部分順次編號就好了。。
看看代碼就懂了。函數
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define MAX 1010 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m; int r[MAX],l[MAX]; int a[MAX][MAX],b[MAX][MAX],c[MAX][MAX]; int S[MAX],top; int main() { n=read();m=read(); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) a[i][j]=read(); for(int i=1;i<=n;++i) { top=0; for(int j=1;j<=m;++j)S[++top]=a[i][j]; sort(&S[1],&S[top+1]);top=unique(&S[1],&S[top+1])-S-1; for(int j=1;j<=m;++j)b[i][j]=lower_bound(&S[1],&S[top+1],a[i][j])-S; r[i]=top; } for(int i=1;i<=m;++i) { top=0; for(int j=1;j<=n;++j)S[++top]=a[j][i]; sort(&S[1],&S[top+1]);top=unique(&S[1],&S[top+1])-S-1; for(int j=1;j<=n;++j)c[j][i]=lower_bound(&S[1],&S[top+1],a[j][i])-S; l[i]=top; } for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=m;++j) { int ans1=max(r[i],max(l[j],b[i][j]+l[j]-c[i][j])); int ans2=max(r[i],max(l[j],c[i][j]+r[i]-b[i][j])); printf("%d ",max(ans1,ans2)); } return 0; }
給定一個串\(S\)以及一個串\(T\)。
如今要求把\(S\)打亂順序,使得\(T\)在\(S\)中出現的次數最多。oop
B比A顯然簡單多了啊,對於\(T\)跑\(KMP\)而後先建一次\(T\),而後每次跳到\(next\)接着日後放就好了。。。ui
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define MAX 500500 char ch[MAX]; int n,m,nt[MAX]; int main() { scanf("%s",ch+1); for(int i=1,l=strlen(ch+1);i<=l;++i) if(ch[i]=='0')++n;else ++m; scanf("%s",ch+1);int l=strlen(ch+1); nt[1]=0; for(int i=2;i<=l;++i) { int t=nt[i-1]; while(t&&ch[t+1]!=ch[i])t=nt[t]; if(!t) { if(ch[1]==ch[i])nt[i]=1; else nt[i]=0; } else nt[i]=t+1; } bool fl=true; for(int i=1;i<=l;++i) if(ch[i]=='0'){if(!n){fl=false;break;}--n;putchar('0');} else{if(!m){fl=false;break;}--m;putchar('1');} while((n||m)&&fl) { for(int i=nt[l]+1;i<=l;++i) if(ch[i]=='0'){if(!n){fl=false;break;}--n;putchar('0');} else{if(!m){fl=false;break;}--m;putchar('1');} } while(n--)putchar('0');while(m--)putchar('1');puts(""); return 0; }
給定一張有向圖,而後你在第\(0\)天從\(1\)號點出發,每一個點都有一個博物館,一週有\(d\)天,每一個博物館會以\(d\)爲週期改變其開放狀態,會告訴你一個串,表示這個點的博物館在第\(t\%d\)天是否開放。
如今你想知道你在\(\infty\)天內最多能夠訪問多少個博物館。spa
首先顯然縮點以後轉\(DAG\),而後設\(f[i][t]\)表示在\(mod\ d\)意義下的第\(t\)天到達\(i\)這個\(scc\)可以訪問到的最大博物館數,若是可以預處理在這個\(scc\)內可以訪問到的博物館數以及出邊,那麼就能夠按照拓撲序直接\(dp\)。
對於每個\(scc\)內每個環大小和\(d\)求一個\(gcd\),那麼若是可以在\(t\)時刻訪問到點\(i\),那麼在\(t+k\ gcd\)時刻也能訪問到\(i\),那麼這樣子就能夠預處理出在\(t\)時刻到達這個\(scc\)上的某個特定點可以訪問到的點數。
至於出邊怎麼處理,首先給同一個\(scc\)上的每一個點按照隨便一棵生成樹的深度模\(gcd\)編個號,僞裝爲\(val[u]\),假設有邊\(u,v\)鏈接着兩個不一樣的\(scc\),令\(G\)爲兩個\(scc\)的\(gcd\)的\(gcd\),那麼這兩個\(scc\)之間就能夠連一個時間爲\((val[u]-val[v]+1)\%G+k\times G\)的邊。
這樣子邊數是\(m*d\),點數是\(n\),時間複雜度就是\(O(m*d+n)\)。
彷佛個人方法的有點小複雜QwQ。翻譯
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define ll long long #define MAX 200200 #define MOD 1000000007 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;} int n,m,ty,d; struct Line{int v,next;}e[MAX]; int h[MAX],cnt=1; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} int dfn[MAX],low[MAX],tim; int S[MAX],top;bool ins[MAX]; int scc,G[MAX];bool vis[MAX]; int val[MAX],GCD,g[MAX],D; char V[MAX][51]; int pre[MAX][51]; void Work(int u,int p) { val[u]=p;vis[u]=true; for(int i=h[u];i;i=e[i].next) { int v=e[i].v;if(G[v]!=scc)continue; if(!vis[v])Work(v,p+1); else GCD=__gcd(GCD,abs(p+1-val[v])); } } int A[MAX]; void Tarjan(int u) { dfn[u]=low[u]=++tim;ins[u]=true;S[++top]=u; for(int i=h[u];i;i=e[i].next) if(!dfn[e[i].v])Tarjan(e[i].v),low[u]=min(low[u],low[e[i].v]); else if(ins[e[i].v])low[u]=min(low[u],dfn[e[i].v]); if(dfn[u]==low[u]) { int v,tot=0;++scc; do{v=S[top--];ins[v]=false;G[v]=scc;A[++tot]=v;}while(u!=v); GCD=D;Work(u,0);g[scc]=GCD; for(int i=0;i<GCD;++i) for(int j=1;j<=tot;++j) for(int k=i;k<D;k+=GCD) if(V[A[j]][k]=='1'){V[A[j]][i]='1';break;} for(int i=0;i<GCD;++i) { for(int j=1;j<=tot;++j) if(V[A[j]][(val[A[j]]+i)%GCD]=='1') ++pre[scc][i]; } } } struct Edge{int v,next,d;}E[MAX*50]; int H[MAX],Cnt=1,dg[MAX]; inline void Add(int u,int v,int d){E[Cnt]=(Edge){v,H[u],d};H[u]=Cnt++;dg[v]++;} int ans,f[MAX][55]; void DP() { memset(f,-63,sizeof(f));f[G[1]][0]=pre[G[1]][0]; queue<int> Q; for(int i=1;i<=scc;++i)if(!dg[i])Q.push(i); while(!Q.empty()) { int u=Q.front();Q.pop(); for(int i=H[u];i;i=E[i].next) if(!--dg[E[i].v])Q.push(E[i].v); for(int t=0;t<D;++t) for(int i=H[u];i;i=E[i].next) { int v=E[i].v,d=E[i].d; f[v][(t+d)%D]=max(f[v][(t+d)%D],f[u][t]+pre[v][(t+d)%g[v]]); } for(int t=0;t<D;++t)ans=max(ans,f[u][t]); } printf("%d\n",ans); } int main() { n=read();m=read();D=read(); for(int i=1,x,y;i<=m;++i)x=read(),y=read(),Add(x,y); for(int i=1;i<=n;++i)scanf("%s",V[i]); for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i); for(int u=1;u<=n;++u) for(int i=h[u];i;i=e[i].next) { if(G[u]==G[e[i].v])continue; int U=G[u],V=G[e[i].v]; int dd=__gcd(g[U],g[V]); int d=(val[u]-val[e[i].v]+1+D)%D; for(int j=d%dd;j<D;j+=dd)Add(U,V,j); } DP(); return 0; }
交互題
有一張圖是由一個長度爲\(t\)的鏈和一個大小爲\(c\)的環中間連上一條邊組成的。
假如這條邊鏈接的是鏈的右端點,和環上的\(T\)點。
令鏈的左端點是\(S\)。
如今在\(S\)處有\(10\)個棋子,編號\(0-9\),每次你可讓任意數量的棋子向出邊方向走一步,交互庫會返回若干個集合,每個集合內的棋子都在同一個位置上,而且這個位置上的全部棋子都在這個集合中。
如今你既不知道\(t\)也不知道\(c\)。你須要使用不超過\(3(t+c)\)次操做使得全部棋子都移動到\(T\)位置上而且返回交互庫done
。code
其實我差很少快作出來了。。。。
首先讓兩個棋子移動,一個每次操做都向前走,另一個每兩次操做才向前走。
當兩個棋子相遇時中止。
把環按照\(T\)點開始沿出邊方向標號。
假設當第二個棋子位於\(T\),即\(0\)位置時,假設第一個棋子在\(p\)位置。
由於第一個棋子每次比第二個棋子多走一步,距離差是\(c-p\)。
因此接下來第二個棋子要移動的步數就是\(c-p\)。因此相遇時兩個棋子在\(c-p\)位置。
由於第二個棋子到達\(T\)時走了\(t\)步,此時第一個棋子走了\(2t\)步,即他在環上走了\(t\)步,因此有\(t\mod c=p\),那麼讓\(10\)個棋子同時向前走,當全部棋子位於同一個點時他們就同時到達了\(T\) 。
即這裏還須要走\(t\)步,而這\(t\)步等價於在環上走了\(p\)步,那麼\(1,2\)兩個棋子就從\(c-p\)走到了\(0\)位置即\(T\)。get
#include<iostream> #include<cstdio> using namespace std; char ch[20]; int Read(){fflush(stdout);int x;scanf("%d",&x);for(int i=1;i<=x;++i)scanf("%s",ch);return x;} int main() { while(233) { printf("next 0\n");Read(); printf("next 0 1\n"); int tot=Read(); if(tot==2)break; } while(233) { printf("next 0 1 2 3 4 5 6 7 8 9\n");fflush(stdout); if(Read()==1)break; } printf("done");fflush(stdout); }
你有一列有\(n\)個車箱的火車,從車頭開始\(1-n\)編號。
如今有\(3\)種操做,第一種是在車頭位置加入\(k\)節車箱,第二種位置是在車尾位置加入\(k\)節車箱,第三種是修改每節車箱的價值。
價值是這樣子來的:一開始全部車箱的價值都是\(0\)(包括中途加入的車箱),而後每次修改會給定\(s,b\),若是當前車箱是從車頭開始數的第\(i\)節,那麼它的價值就會加上\((i-1)*s+b\)。
在每次操做結束以後回答價值最小的車箱的編號以及其價值。若是有多個輸出編號最小的那個。
發現每次一塊兒加入進來的東西只須要維護左端點就行了。
首先若是在前端插入一段,那麼後面所有都沒用了,能夠直接丟到。
不然在後面插入一段,維護一下當前全局加的一次函數是什麼,那麼每一個左端點按照車箱編號+權值能夠寫成一個個的點,顯然只有一個上凸殼纔有用,那麼維護這個上凸殼就好了。
#include<iostream> #include<cstdio> using namespace std; #define MAX 300300 #define ll long long #define pi pair<ll,ll> #define fr first #define sd second #define mp make_pair inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } pi p[MAX]; int n,m,r=1;ll k=0,b=0; double Slope(pi a,pi b){return 1.0*(a.sd-b.sd)/(a.fr-b.fr);} ll calc(pi x){return k*x.fr+x.sd+b;} int main() { n=read();m=read();p[1]=mp(0,0);r=1; while(m--) { int opt=read(); if(opt==1)r=1,p[1]=mp(0,0),k=b=0,n+=read(); else if(opt==2) { pi now=mp(n,-(n*k+b)); while(r>1&&Slope(now,p[r])<=Slope(p[r],p[r-1]))--r; p[++r]=now;n+=read(); } else b+=read(),k+=read(); while(r>1&&calc(p[r])>=calc(p[r-1]))--r; printf("%I64d %I64d\n",p[r].fr+1,calc(p[r])); } return 0; }
這題目名稱真帶感
有一個善良的人不想燒樹,因此她決定在腦子裏模擬燒樹的過程。
咱們認爲樹在燃燒的過程是這樣的:每個點有一個獨一無二的優先級,每次燒掉優先級最小的那個葉子節點。
如今有若干次操做:
要麼把一個點的優先級變爲全局優先級的最大值+1,要麼詢問一個點何時被燒掉,要麼詢問兩個點誰先被燒掉。
初始時\(i\)號點的優先級是\(i\)。
顯然兩個詢問是同樣的東西。
加入咱們已經知道了點與點之間的燃燒順序,那麼考慮一次修改操做對於燃燒次序的影響。
假設修改的點是\(x\),修改前優先級最大的點是\(y\)。
顯然除了\(x-y\)這條鏈以外,其餘點的燃燒相對循序是不變的。
在燒完其餘的點以後會從\(y\)一直燒到\(x\)。
咱們考慮一次修改操做,認爲它就是把\(x-y\)鏈上的全部點從新染上一種顏色。
那麼回答\(x\)被燃燒的時間,就是顏色編號比它小的點的個數再來加上顏色相同且要在他以前燃燒的點的數量。
那麼咱們用一個\(LCT\)來維護這個操做,強制每次\(LCT\)的根節點都是當前全局顏色最大的節點。
這樣子修改點的優先級的時候,只須要直接\(makeroot\)就好了。
那樹狀數組維護顏色的前綴和,那麼詢問一個點的時候就是全部顏色不小於它的點數,再減去其在\(LCT\)上同色的祖先,即\(Splay\)以後的左子樹大小。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; #define ll long long #define MAX 200200 #define ls (t[x].ch[0]) #define rs (t[x].ch[1]) inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct BIT { int c[MAX<<1],n; int lb(int x){return x&(-x);} void Modify(int x,int w){while(x<=n)c[x]+=w,x+=lb(x);} int Query(int x){int s=0;while(x)s+=c[x],x-=lb(x);return s;} }C; struct Node{int ch[2],ff,rev,sz,col,tag;}t[MAX]; bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;} void pushup(int x){t[x].sz=t[ls].sz+t[rs].sz+1;} void rotate(int x) { int y=t[x].ff,z=t[y].ff; int k=t[y].ch[1]==x; if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z; t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y; t[x].ch[k^1]=y;t[y].ff=x; pushup(y);pushup(x); } void putrev(int x){swap(ls,rs);t[x].rev^=1;} void puttag(int x,int w){t[x].col=t[x].tag=w;} void pushdown(int x) { if(t[x].rev)putrev(ls),putrev(rs),t[x].rev^=1; if(t[x].tag)puttag(ls,t[x].tag),puttag(rs,t[x].tag),t[x].tag=0; } int S[MAX],top; void Splay(int x) { S[top=1]=x; for(int i=x;!isroot(i);i=t[i].ff)S[++top]=t[i].ff; while(top)pushdown(S[top--]); while(!isroot(x)) { int y=t[x].ff,z=t[y].ff; if(!isroot(y)) (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y); rotate(x); } } void access(int x,int col) { for(int y=0;x;y=x,x=t[x].ff) { Splay(x),rs=0,pushup(x); C.Modify(t[x].col,-t[x].sz); puttag(x,col); C.Modify(t[x].col,t[x].sz); rs=y;pushup(x); } } int When(int x){Splay(x);return C.Query(t[x].col)-t[ls].sz;} struct Line{int v,next;}e[MAX<<1]; int h[MAX],cnt=1; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} int n,Q;char ch[10]; void Build(int x,int ff) { t[x].col=x;t[x].sz=1;t[x].ff=ff; for(int i=h[x];i;i=e[i].next) { int v=e[i].v;if(v==ff)continue; Build(v,x);t[x].col=max(t[x].col,t[v].col); } for(int i=h[x];i;i=e[i].next) if(e[i].v!=ff&&t[x].col==t[e[i].v].col) rs=e[i].v,t[x].sz+=t[e[i].v].sz; C.Modify(t[x].col,1); } int main() { n=read();Q=read();C.n=n+Q+1; for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u); Build(n,0);int color=n; while(Q--) { scanf("%s",ch); if(ch[0]=='u') { int x=read(); access(x,color); Splay(x);putrev(x);pushdown(x); C.Modify(t[x].col,-1); rs=0;puttag(x,++color); t[x].sz=1; C.Modify(t[x].col,1); } else if(ch[0]=='w')printf("%d\n",When(read())); else { int x=read(),y=read(); if(When(x)<When(y))printf("%d\n",x); else printf("%d\n",y); } } return 0; }