B题打错调了半天,C题想出来来不及打,还好没有挂题
AC:AB Rank:96 Rating:2125+66->2191
A.Berzerk
题目大意:有一个东东在长度为n的环上(环上点编号0~n-1),两个玩家,玩家1有a种操作可选,玩家2有b种操作可选,每种操作可以让这个东东向前走若干步,两个玩家轮流操作,谁先让东东走到0谁胜,求出双方都选最优操作的情况下,东东开始在1~n-1各位置时玩家1先手和玩家2先手会必胜,必败还是无限循环。(a,b<n<=7000)
思路:类似DFS或者BFS的乱找,每次确定一个必败状态时,能到该状态的所有状态都为必胜态,一个状态能到的所有状态都确定为必胜时,该状态必败,用数组记下每个状态还有几个能到的状态未被确定即可,最后无法确定的状态即为循环,总复杂度O((a+b)n)。
#include<cstdio> char B[1<<26],*S=B,C;int X; inline int read() { while((C=*S++)<'0'||C>'9'); for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0'; return X; } #define MN 7000 int n,s[2],t[2][MN+5],u[MN*2+5],q[MN*2+5],l[MN*2+5],qn; int d(int x,int y){return x*n+(y+n)%n;} void lose(int); void win(int p) { int x=p/n,y=p%n,i,v; x^=1;u[p]=2; for(i=1;i<=s[x];++i) { v=d(x,y-t[x][i]); if(!u[v]&&++l[v]==s[x])lose(v); } } void lose(int p) { int x=p/n,y=p%n,i,v; x^=u[p]=1; for(i=1;i<=s[x];++i) { v=d(x,y-t[x][i]); if(!u[v])win(v); } } int main() { fread(B,1,1<<26,stdin); int i,x,y; for(n=read(),x=0;x<2;++x)for(s[x]=read(),i=1;i<=s[x];++i)t[x][i]=read(); u[n]=1;lose(0);lose(n); for(i=1;i<n;++i)printf("%s ",u[i]?u[i]>1?"Win":"Lose":"Loop");puts(""); for(i=1;i<n;++i)printf("%s ",u[i+n]?u[i+n]>1?"Win":"Lose":"Loop"); }
B.Legacy
题目大意:n个点,一个人一开始位于s,有q个走法供他选择,走法有3种种类:1.从v到u,花费w;2.从v到l~r中的一个点,花费w;3.从l~r中的一个点到v,花费w,求到各个点的最短路。(n,q<=100,000)
思路:线段树优化建图。第一类边直接连;第二类我们建一棵线段树,所有父亲向儿子连长度为0的边,表示到了该区间也能到达该区间中的点,每次我们让v连向表示[l,r]的O(log)个线段树节点即可;第三类我们再建一棵线段树,所有儿子向父亲连长度为0的边,这样每个点就能到达所有表示包含它的区间的线段树节点,每次我们让表示[l,r]的线段树上节点连向v即可。跑Dijkstra,总复杂度O((n+q)logn^2)。
#include<cstdio> #include<cstring> #include<queue> using namespace std; #define ll long long char B[1<<26],*S=B,C;int X; inline int read() { while((C=*S++)<'0'||C>'9'); for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0'; return X; } #define MN 100000 #define N 131072 #define MV 524288 #define ME 5000000 #define d(x,y) make_pair(x,y) struct edge{int nx,t,w;}e[ME+5]; int h[MV+5],en;ll ds[MV+5]; typedef pair<ll,int> data; priority_queue<data,vector<data>,greater<data> >pq; inline void ins(int x,int y,int w){e[++en]=(edge){h[x],y,w};h[x]=en;} void ins1(int l,int r,int f,int w) { for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1)ins(f+N,l+1,w); if( r&1)ins(f+N,r-1,w); } } void ins2(int l,int r,int t,int w) { for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1)ins(l+1+(N<<1),t+N,w); if( r&1)ins(r-1+(N<<1),t+N,w); } } int main() { fread(B,1,1<<26,stdin); int n,m,i,s,a,b,c,d;ll x; n=read();m=read();s=read(); for(i=1;i<N;++i) { ins(i,i<<1,0);ins(i,i<<1|1,0); ins(i+N<<1,i+(N<<1),0);ins(i+N<<1|1,i+(N<<1),0); ins(i+N,i+N*3,0);ins(i+N*3,i+N,0); } while(m--) { a=read();b=read();c=read();d=read(); if(a==1)ins(b+N,c+N,d); if(a==2)ins1(c,d,b,read()); if(a==3)ins2(c,d,b,read()); } memset(ds,127,sizeof(ds));ds[s+N]=1;pq.push(d(0,s+N)); while(pq.size()) { a=pq.top().second;x=pq.top().first;pq.pop(); for(i=h[a];i;i=e[i].nx)if(x+e[i].w<ds[e[i].t]) { ds[e[i].t]=x+e[i].w; pq.push(d(ds[e[i].t],e[i].t)); } while(pq.size()&&pq.top().first>ds[pq.top().second])pq.pop(); } for(i=1;i<=n;++i)printf("%I64d ",ds[i+N]<ds[0]?ds[i+N]:-1); }
C.Till I Collapse
题目大意:给定n个数,对于每个1<=k<=n,求把数列分成若干段,每段数字种数不超过k,至少分几段。(n<=100,000)
思路:对于k<=n^0.5,我们每次O(n)暴力统计答案,对于k>n^0.5,我们先预处理出k=n^0.5时分成的至多n^0.5段,每段的右端点和各种数字的出现次数,每次把所有右端点向右推即可知道k+1时的信息,右端点每向右推一位我们都只要O(1),总复杂度O(n^1.5)。
#include<cstdio> inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 100000 #define MK 320 int a[MN+5],f[MK+5][MN+5],cnt[MK+5],r[MK+5],ans; int main() { int n=read(),i,j,k; for(i=1;i<=n;++i)a[i]=read(); for(i=1;i<=n&&i<=MK;++i) { for(ans=j=0;j++<=n;) if(j>n||(!f[0][a[j]]++&&++cnt[0]>i)) { for(k=j>n?n:j;cnt[0];--k)if(!--f[0][a[k]])--cnt[0]; if(j<=n)f[0][a[j]]=cnt[0]=1;++ans; } printf("%d ",ans); } if(i<=n) { for(ans=j=1;j<=n;r[ans]=++j) if(!f[ans][a[j]]++&&++cnt[ans]>i) { --f[ans][a[j]];--cnt[ans]; ++f[++ans][a[j]];++cnt[ans]; } printf("%d ",ans); } for(++i;i<=n;++i) { for(j=1;j<=ans;++j) { for(k=r[j];k<=n;r[j]=++k) { if(!--f[j+1][a[k]])--cnt[j+1]; if(!f[j][a[k]]++&&++cnt[j]>i) { if(!--f[j][a[k]])--cnt[j]; if(!f[j+1][a[k]]++)++cnt[j+1]; break; } } if(k>n)ans=j; } printf("%d ",ans); } }