Codeforces Round #406 (Div. 1)
CF786A Berzerk
分析
博弈一般要从终止状态即黑洞开始,如果先手必败,那么后手在上一位置必胜,
如果先手无论怎么走都会在后手必胜的位置,那么先手必败
能转移到必败态为必胜态,只能转移到必胜态为必败态
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=7011;
int v[2][N],a[2][N],n,win[2][N],cnt[2][N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void dfs(int who,int now){
if (v[who][now]) return; v[who][now]=1;
for (rr int i=1;i<=a[who^1][0];++i){
rr int x=(now+a[who^1][i])%n; if (!x) continue;
if (!win[who][now]) win[who^1][x]=1,dfs(who^1,x);
else if (++cnt[who^1][x]==a[who^1][0])
win[who^1][x]=0,dfs(who^1,x);
}
}
signed main(){
n=iut();
a[0][0]=iut(); for (rr int i=1;i<=a[0][0];++i) a[0][i]=n-iut();
a[1][0]=iut(); for (rr int i=1;i<=a[1][0];++i) a[1][i]=n-iut();
dfs(0,0),dfs(1,0);
for (rr int i=1;i<n;++i){
if (!v[0][i]) printf("Loop");
else if (win[0][i]) printf("Win");
else printf("Lose");
putchar(i==n-1?10:32);
}
for (rr int i=1;i<n;++i){
if (!v[1][i]) printf("Loop");
else if (win[1][i]) printf("Win");
else printf("Lose");
putchar(i==n-1?10:32);
}
return 0;
}
CF786B Legacy
分析
考虑最短路径,但是直接建边边数在\(O(nm)\)级别内,
考虑建两棵线段树,一棵下行边权为0,一棵上行边权为0,
对于\((x,y,w)\)直接连边即可,
对于点\(x\)连向\([l,r]\),将区间拆成\(O(logn)\)个线段树节点,
并将点\(x\)连向下行的\([l,r]\)线段树中,
同理,上行的\([l',r']\)连向点\(x'\)中,这样边数为\(O(mlogn)\),
加上Dijkstra的时间复杂度为\(O(mlog^2n)\)
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=300011; typedef long long lll;
struct node{int y,w,next;}e[N<<3]; pair<lll,int>heap[N];
int Cnt,n,m,S,as[N],et=1,cnt,ls[N],rs[N],root; lll dis[N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(lll ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void add(int x,int y,int w){e[++et]=(node){y,w,as[x]},as[x]=et;}
inline void Push(pair<lll,int>w){
heap[++Cnt]=w;
rr int x=Cnt;
while (x>1){
if (heap[x]<heap[x>>1])
swap(heap[x],heap[x>>1]),x>>=1;
else return;
}
}
inline void Pop(){
heap[1]=heap[Cnt--];
rr int x=1;
while ((x<<1)<=Cnt){
rr int y=x<<1;
if (y<Cnt&&heap[y+1]<heap[y]) ++y;
if (heap[x]>heap[y]) swap(heap[x],heap[y]),x=y;
else return;
}
}
inline void Dijkstra(int S){
heap[++Cnt]=make_pair(0,S);
for (rr int i=1;i<=cnt;++i) dis[i]=1e15; dis[S]=0;
while (Cnt){
rr lll t=heap[1].first; rr int x=heap[1].second;
Pop(); if (t!=dis[x]) continue;
for (rr int i=as[x];i;i=e[i].next)
if (dis[e[i].y]>dis[x]+e[i].w){
dis[e[i].y]=dis[x]+e[i].w;
Push(make_pair(dis[e[i].y],e[i].y));
}
}
}
inline void build(int &k,int l,int r){
if (l==r) {k=l; return;}
if (!k) k=++cnt,++cnt;
rr int mid=(l+r)>>1;
build(ls[k],l,mid);
build(rs[k],mid+1,r);
add(k,ls[k],0),add(k,rs[k],0);
if (l==mid) add(ls[k],k^1,0);
else add(ls[k]^1,k^1,0);
if (mid+1==r) add(rs[k],k^1,0);
else add(rs[k]^1,k^1,0);
}
inline void update(int k,int l,int r,int x,int y,int z,int w){
if (l==r){
if (w<0) add(l,z,-w);
else add(z,l,w);
return;
}
if (l==x&&r==y){
if (w<0) add(k^1,z,-w);
else add(z,k,w);
return;
}
rr int mid=(l+r)>>1;
if (y<=mid) update(ls[k],l,mid,x,y,z,w);
else if (x>mid) update(rs[k],mid+1,r,x,y,z,w);
else update(ls[k],l,mid,x,mid,z,w),update(rs[k],mid+1,r,mid+1,y,z,w);
}
signed main(){
n=iut(),m=iut(),S=iut(),cnt=n;
if (!(cnt&1)) ++cnt;
build(root,1,n);
for (rr int i=1;i<=m;++i)
switch (iut()){
case 1:{
rr int x=iut(),y=iut(),w=iut();
add(x,y,w);
break;
}
case 2:{
rr int x=iut(),l=iut(),r=iut(),w=iut();
if (l==r) add(x,l,w);
else update(root,1,n,l,r,x,w);
break;
}
case 3:{
rr int x=iut(),l=iut(),r=iut(),w=iut();
if (l==r) add(l,x,w);
else update(root,1,n,l,r,x,-w);
break;
}
}
Dijkstra(S);
for (rr int i=1;i<=n;++i){
if (dis[i]==1e15) putchar('-'),putchar(49);
else print(dis[i]);
putchar(i==n?10:32);
}
return 0;
}
CF786C Till I Collapse
分析
设 \(dp[x]\) 表示划分 \(x\sim n\) 的最小段数,
那么 \(x\) 能够转移到 \(y<x\) 且 \(col[y+1,x]=k\) 的最小的 \(y\)
这个可以用主席树维护,并且对于 \(k\),最多分为 \(\frac{n}{k}\) 段,
那么总时间复杂度应为 \(O(n\log^2n)\)
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011;
int n,cnt,rt[N],pre[N],ls[N<<6],rs[N<<6],w[N<<6];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void updzro(int &k,int l,int r,int x){
++cnt,ls[cnt]=ls[k],rs[cnt]=rs[k],w[cnt]=w[k],k=cnt;
if (l==r) {w[k]=0; return;}
rr int mid=(l+r)>>1;
if (x<=mid) updzro(ls[k],l,mid,x);
else updzro(rs[k],mid+1,r,x);
w[k]=w[ls[k]]+w[rs[k]];
}
inline void update(int &k,int l,int r,int x){
++cnt,ls[cnt]=ls[k],rs[cnt]=rs[k],w[cnt]=w[k],k=cnt;
if (l==r) {w[k]=1; return;}
rr int mid=(l+r)>>1;
if (x<=mid) update(ls[k],l,mid,x);
else update(rs[k],mid+1,r,x);
w[k]=w[ls[k]]+w[rs[k]];
}
inline signed query(int k,int l,int r,int x){
if (l==r) return l;
rr int mid=(l+r)>>1;
if (w[rs[k]]>=x) return query(rs[k],mid+1,r,x);
else return query(ls[k],l,mid,x-w[rs[k]]);
}
signed main(){
n=iut();
for (rr int i=1;i<=n;++i){
rr int x=iut();
if (pre[x]) updzro(rt[i]=rt[i-1],1,n,pre[x]);
else rt[i]=++cnt,ls[rt[i]]=ls[rt[i-1]],rs[rt[i]]=rs[rt[i-1]];
update(rt[i],1,n,i);
pre[x]=i;
}
for (rr int i=1;i<=n;++i){
rr int ans=0,now=n;
for (;now;++ans,now=query(rt[now],1,n,i+1))
if (w[rt[now]]<=i){++ans; break;}
print(ans),putchar(i==n?10:32);
}
return 0;
}
CF786D Rap God
分析
正解是点分治+trie+哈希,但是可以用 \(O(qn)\) 草过去
如果 \(O(qn)\) 不能带 trie,要直接先传到根再传下去
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=20011; struct node{int y,w,next;}e[N<<1];
int dfn[N],fat[N],tot,upd,is[N],v[N],len[N],nfd[N],dep[N],et=1,S[N],T[N],w[N],n,m,as[N];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void dfs(int x,int fa){
dfn[x]=++tot,fat[x]=fa,nfd[tot]=x,dep[x]=dep[fa]+1;
for (rr int i=as[x];i;i=e[i].next)
if (e[i].y!=fa) w[e[i].y]=e[i].w,dfs(e[i].y,x);
}
signed main(){
n=iut(),m=iut();
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut();
rr char ch=getchar();
e[++et]=(node){y,ch-97,as[x]},as[x]=et;
e[++et]=(node){x,ch-97,as[y]},as[y]=et;
}
dfs(1,0);
for (rr int i=1,x,y,X,Y;i<=m;++i){
x=X=iut(),y=Y=iut(),S[0]=T[0]=0;
while (X!=Y)
if (dep[X]<dep[Y]) T[++T[0]]=w[Y],Y=fat[Y];
else S[++S[0]]=w[X],X=fat[X];
for (;T[0];--T[0]) S[++S[0]]=T[T[0]];
rr int ans=0; len[x]=0,v[x]=i,is[x]=-1;
for (rr int X=x;fat[X];X=fat[X]){
v[fat[X]]=i,len[fat[X]]=len[X]+1;
if (~is[X]) is[fat[X]]=is[X];
else if (len[fat[X]]>S[0]) is[fat[X]]=0;
else if (S[len[fat[X]]]>w[X]) is[fat[X]]=1;
else if (S[len[fat[X]]]<w[X]) is[fat[X]]=0;
else is[fat[X]]=-1;
if (is[fat[X]]==1||(is[fat[X]]==-1&&len[fat[X]]<S[0])) ++ans;
}
for (rr int j=2;j<=n;++j)
if (v[nfd[j]]!=i){
X=nfd[j],len[X]=len[fat[X]]+1;
if (~is[fat[X]]) is[X]=is[fat[X]];
else if (len[X]>S[0]) is[X]=0;
else if (S[len[X]]>w[X]) is[X]=1;
else if (S[len[X]]<w[X]) is[X]=0;
else is[X]=-1;
if (is[X]==1||(is[X]==-1&&len[X]<S[0])) ++ans;
}
print(ans),putchar(10);
}
return 0;
}
CF786E ALT
分析
源点连居民,汇点连守卫,考虑最小割,线段树优化建图,
图不连通当且仅当居民分到宠物或者居民所对应的守卫都能分到宠物
代码
#include <cstdio>
#include <cctype>
#include <queue>
#define rr register
using namespace std;
const int N=60011,inf=1e9; struct node{int y,w,next;}e[N<<4],E[N];
int dis[N],v[N],fat[N],dfn[N],Top[N],ls[N],rs[N],as[N],hs[N],big[N],siz[N],dep[N];
int n,et=1,S,T,Et=1,m,rt,cnt,tot,ans,nfd[N],ans1[N],ans2[N],W[N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void add(int x,int y,int w){
e[++et]=(node){y,w,as[x]},as[x]=et;
e[++et]=(node){x,0,as[y]},as[y]=et;
}
inline bool bfs(int st){
for (rr int i=1;i<=T;++i) dis[i]=0;
rr queue<int>q; q.push(st),dis[st]=1;
while (!q.empty()){
rr int x=q.front(); q.pop();
for (rr int i=as[x];i;i=e[i].next)
if (e[i].w>0&&!dis[e[i].y]){
dis[e[i].y]=dis[x]+1;
if (e[i].y==T) return 1;
q.push(e[i].y);
}
}
return 0;
}
inline signed min(int a,int b){return a<b?a:b;}
inline signed dfs(int x,int now){
if (x==T||!now) return now;
rr int rest=0,f;
for (rr int i=as[x];i;i=e[i].next)
if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
f=dfs(e[i].y,min(now-rest,e[i].w)),
rest+=f,e[i].w-=f,e[i^1].w+=f;
if (now==rest) return now;
}
if (!rest) dis[x]=0;
return rest;
}
inline void build(int &k,int l,int r){
if (l==r) {k=l; return;}
k=++cnt; rr int mid=(l+r)>>1;
build(ls[k],l,mid),build(rs[k],mid+1,r);
add(k,ls[k],inf),add(k,rs[k],inf);
}
inline void update(int k,int l,int r,int x,int y,int z){
if (l==x&&r==y) {add(z,k,inf); return;}
rr int mid=(l+r)>>1;
if (y<=mid) update(ls[k],l,mid,x,y,z);
else if (x>mid) update(rs[k],mid+1,r,x,y,z);
else update(ls[k],l,mid,x,mid,z),update(rs[k],mid+1,r,mid+1,y,z);
}
inline void dfs1(int x,int fa){
dep[x]=dep[fa]+1,fat[x]=fa,siz[x]=1;
for (rr int i=hs[x],SIZ=-1;i;i=E[i].next)
if (E[i].y!=fa){
W[E[i].y]=i,dfs1(E[i].y,x),siz[x]+=siz[E[i].y];
if (SIZ<siz[E[i].y]) SIZ=siz[E[i].y],big[x]=E[i].y;
}
}
inline void dfs2(int x,int linp){
dfn[x]=++tot,nfd[tot]=x,Top[x]=linp;
if (!big[x]) return; dfs2(big[x],linp);
for (rr int i=hs[x];i;i=E[i].next)
if (E[i].y!=big[x]&&E[i].y!=fat[x])
dfs2(E[i].y,E[i].y);
}
inline void dfs3(int x,int fa){
v[x]=1;
for (rr int i=as[x];i;i=e[i].next)
if (!v[e[i].y]&&e[i].w) dfs3(e[i].y,x);
}
signed main(){
n=iut(),m=iut(),cnt=n+m,
build(rt,1,n),S=cnt+1,T=S+1;
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut();
E[++Et]=(node){y,0,hs[x]},hs[x]=Et;
E[++Et]=(node){x,0,hs[y]},hs[y]=Et;
}
for (rr int i=1;i<=m;++i) add(S,i+n,1);
for (rr int i=1;i<=n;++i) add(i,T,1);
dfs1(1,0),dfs2(1,1);
for (rr int i=1;i<=m;++i){
rr int x=iut(),y=iut();
for (;Top[x]!=Top[y];x=fat[Top[x]]){
if (dep[Top[x]]<dep[Top[y]]) x^=y,y^=x,x^=y;
update(rt,1,n,dfn[Top[x]],dfn[x],i+n);
}
if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
if (dep[x]<dep[y]) update(rt,1,n,dfn[x]+1,dfn[y],i+n);
}
while (bfs(S)) ans+=dfs(S,inf); dfs3(S,0);
printf("%d\n",ans);
for (rr int i=as[S];i;i=e[i].next)
if (!v[e[i].y]) ans1[++ans1[0]]=e[i].y-n;
for (rr int i=as[T];i;i=e[i].next)
if (v[e[i].y]) ans2[++ans2[0]]=W[nfd[e[i].y]]>>1;
for (rr int i=0;i<=ans1[0];++i) printf("%d%c",ans1[i],i==ans1[0]?10:32);
for (rr int i=0;i<=ans2[0];++i) printf("%d%c",ans2[i],i==ans2[0]?10:32);
return 0;
}