2016-2017 ACM-ICPC Asia-Bangkok Regional Contest
A. WSI Extreme
将人按洗澡时间从大到小排序,那么$ans=\sum_{i=1}^{n}a_i\times\lfloor\frac{i+W-1}{W}\rfloor$。
当$W$比较大时,暴力枚举每一段,然后求和即可,权值线段树维护。
当$W$比较小时,线段树上按排名模$W$的值维护$W$个量即可。
时间复杂度$O(q\sqrt{W}\log n)$。
B. Average
枚举平均数然后容斥+隔板法计算方案数即可。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; const int mod=1e9+7,Maxn=60*202; void up(int &x,int y){x+=y;if(x>=mod)x-=mod;} int rev[Maxn],fac[Maxn]; int C(int x,int y){ if(x<y||y<0)return 0; return 1LL*fac[x]*rev[y]%mod*rev[x-y]%mod; } void solve(){ fac[0]=fac[1]=rev[0]=rev[1]=1; for(int i=2;i<Maxn;i++)fac[i]=1LL*fac[i-1]*i%mod; for(int i=2;i<Maxn;i++)rev[i]=1LL*(mod-mod/i)*rev[mod%i]%mod; for(int i=2;i<Maxn;i++)rev[i]=1LL*rev[i-1]*rev[i]%mod; } int check(int sum,int n,int m){ int ret=0; for(int i=0;i<=n;i++){ int tmp=1LL*C(n,i)*C(sum+n-i*(m+1)-1,n-1)%mod; //if(i==1)printf("t1=%d t2=%d\n",C(n,i),C(sum+n-i*(m+1)-1,n-1)); if(i&1)tmp=(mod-tmp)%mod; up(ret,tmp); //printf("ret=%d\n",ret); } return ret; } int main () { solve(); //printf("t2=%d\n",check(2,2,1)); int n,m; while(scanf("%d%d",&n,&m)!=EOF){ if(!n&&!m)break; int ans=0; for(int i=0;i<=m;i++){ up(ans,1LL*check((n-1)*i,n-1,m)%mod); } //printf("t1=%d\n",check(0,2,1)); //printf("t2=%d\n",check(2,2,1)); ans=1LL*ans*n%mod; printf("%d\n",ans); } return 0 ; }
C. Big Bang
$ans=\sum_{i=1}^n\mu(i)\lfloor\frac{n}{i}\rfloor^4$。
D. Find C
首先通过exgcd求出一组解,然后往两侧延伸$K$个即可。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; const LL LIM=100000000000000LL; typedef pair<LL,LL>pi; LL sx,sy,ex,ey; int K; LL gcd(LL x,LL y){ if(!x)return y; if(!y)return x; return __gcd(x,y); } inline LL exgcd(LL a,LL b,LL &x,LL &y){ if(!b)return x=1,y=0,a; LL d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } int ok(LL x,LL y){ if(abs(x)>LIM||abs(y)>LIM)return 0; return 1; } bool check(LL x,LL y){ if(x==sx&&y==sy)return 0; if(x==ex&&y==ey)return 0; if(__gcd(abs(x-sx),abs(y-sy))>1)return 0; if(__gcd(abs(x-ex),abs(y-ey))>1)return 0; return 1; } /* void getst(LL &x,LL &y,LL stepx,LL stepy){ if(!stepx){ if(stepy<0){ y%=-stepy; } else { y%=stepy; } } else if(!stepy){ x%=stepx; } else{ if(stepy>0){ LL } } } */ set<pi>res; void solve(LL S){ LL A=abs(sy-ey),B=abs(sx-ex); LL gc=gcd(A,B); LL tA=(sy-ey)/gc,tB=(ex-sx)/gc; LL ftA=abs(tA),ftB=abs(tB); LL C=S-(sx*ey-ex*sy); C/=gc; LL x,y; exgcd(ftA,ftB,x,y); if(ftB){ x=x*(C%ftB)%ftB; y=(C-x*ftA)/ftB; } else{ y=y*(C%ftA)%ftA; x=(C-y*ftB)/ftA; } if(tA<0)x=-x; if(tB<0)y=-y; LL stepx=(ex-sx)/gc,stepy=(ey-sy)/gc; //if(!ok(x,y))while(1); for(LL curx=x+stepx,cury=y+stepy;res.size()<K&&ok(curx,cury);curx+=stepx,cury+=stepy){ if(check(curx,cury)){ //printf("curx=%lld cury=%lld\n",curx,cury); res.insert(pi(curx,cury)); } } for(LL curx=x,cury=y;res.size()<K&&ok(curx,cury);curx-=stepx,cury-=stepy){ if(check(curx,cury)){ res.insert(pi(curx,cury)); } } } int main () { int _;scanf("%d",&_); while(_--){ scanf("%lld%lld%lld%lld%d",&sx,&sy,&ex,&ey,&K); if(ex<sx){swap(sx,ex);swap(sy,ey);} res.clear(); LL S=__gcd(abs(sx-ex),abs(sy-ey)); solve(S); solve(-S); if(res.size()<K)while(1); for(set<pi>::iterator it=res.begin();it!=res.end();it++){ printf("%lld %lld\n",it->first,it->second); } } return 0 ; }
E. ACM Tax
可持久化线段树维护即可。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> using namespace std; typedef long long ll; const int N=50010,M=N*20; int T,C; int n,m,i,x,y,z,g[N],v[N<<1],w[N<<1],nxt[N<<1],ed; int f[N],d[N],size[N],son[N],top[N]; int tot,rt[N],l[M],r[M],val[M]; inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} int ins(int x,int a,int b,int c){ int y=++tot; val[y]=val[x]+1; if(a==b)return y; int mid=(a+b)>>1; if(c<=mid){ l[y]=ins(l[x],a,mid,c); r[y]=r[x]; }else{ l[y]=l[x]; r[y]=ins(r[x],mid+1,b,c); } return y; } void dfs(int x){ size[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x,d[v[i]]=d[x]+1; rt[v[i]]=ins(rt[x],1,100000,w[i]); dfs(v[i]),size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]); } inline int lca(int x,int y){ for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y); return d[x]<d[y]?x:y; } inline int kth(int x,int y,int z,int k){ int a=1,b=100000,mid,t; while(a<b){ mid=(a+b)>>1; t=val[l[x]]+val[l[y]]-2*val[l[z]]; if(k<=t){ b=mid; x=l[x]; y=l[y]; z=l[z]; }else{ k-=t; a=mid+1; x=r[x]; y=r[y]; z=r[z]; } } return a; } inline int query(int x,int y){ int z=lca(x,y); int all=d[x]+d[y]-d[z]*2; if(all&1)return kth(rt[x],rt[y],rt[z],(all+1)/2)*2; return kth(rt[x],rt[y],rt[z],all/2)+kth(rt[x],rt[y],rt[z],all/2+1); } int main(){ scanf("%d",&T); for(C=1;C<=T;C++){ scanf("%d",&n); ed=0; for(i=1;i<=n;i++)g[i]=f[i]=d[i]=size[i]=son[i]=top[i]=rt[i]=0; for(i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z); dfs(1); dfs2(1,1); scanf("%d",&m); while(m--)scanf("%d%d",&x,&y),printf("%.1f\n",0.5*query(x,y)); for(i=1;i<=tot;i++)l[i]=r[i]=val[i]=0; tot=0; } }
F. Dictionary Game
建出Trie树,然后就是经典树上删边游戏。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> using namespace std; typedef long long ll; const int N=100010*40; int T,C,tot,son[N][26],sg[N],f[N]; int n,i,j; inline int ins(){ static char s[99]; scanf("%s",s); int l=strlen(s); int x=0; for(int i=0;i<l;i++){ int w=s[i]-'a'; if(!son[x][w]){ son[x][w]=++tot; f[tot]=x; } x=son[x][w]; } return x; } inline void cal(int x){ sg[x]=0; for(int i=0;i<26;i++)if(son[x][i])sg[x]^=sg[son[x][i]]+1; } void dfs(int x){ for(int i=0;i<26;i++)if(son[x][i])dfs(son[x][i]); cal(x); } inline void up(int x){ while(x){ cal(x); x=f[x]; } cal(0); } int main(){ scanf("%d",&T); for(C=1;C<=T;C++){ printf("Case %d:\n",C); scanf("%d",&n); while(n--)ins(); dfs(0); scanf("%d",&n); while(n--){ up(ins()); puts(sg[0]?"1":"2"); } for(i=0;i<=tot;i++)for(j=f[i]=sg[i]=0;j<26;j++)son[i][j]=0; tot=0; } }
G. Binary Strings
矩阵快速幂优化DP转移。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; const int mod=1e9+7; void up(int &x,int y){x+=y;if(x>=mod)x-=mod;} struct Mat{ int a[3][3]; Mat operator *(const Mat&x)const{ Mat ret; for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ int t=0; for(int k=0;k<3;k++){ up(t,1LL*a[i][k]*x.a[k][j]%mod); } ret.a[i][j]=t; } } return ret; } void init(int t){ for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ a[i][j]=i==j?t:0; } } } }ori; Mat powmod(Mat x,LL y){ Mat ret; ret.init(1); while(y){ if(y&1){ret=ret*x;} y>>=1; x=x*x; } return ret; } int go(LL x){ Mat tmp=powmod(ori,x+1); return tmp.a[0][2]; } void solve(){ LL l,r,k; scanf("%lld%lld%lld",&l,&r,&k); ori.init(0); ori.a[0][0]=1; ori.a[0][1]=1; ori.a[1][0]=1; ori=powmod(ori,k); ori.a[0][2]=1; ori.a[1][2]=1; ori.a[2][2]=1; ori.a[2][0]=ori.a[2][1]=0; int ans=go(r/k); ans-=go((l-1)/k); if(ans<0)ans+=mod; printf("%d\n",ans); } int main () { int _,cas=1;scanf("%d",&_); while(_--){ printf("Case %d: ",cas++); solve(); } return 0 ; }
H. Witcher Potion
$f[S][i][j][k]$表示已经使用过的药水集合为$S$,当前能量为$i$,毒性为$j$,上一次是否嗑药为$k$时最多能打多少只怪物,然后DP即可。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> using namespace std; typedef long long ll; const int inf=10000000; int T,K,M,n,i,j,k,ans,t,e[11],p[11],f[1<<8][111][111][2],o; inline void up(int&x,int y){if(x<y)x=y;} int main(){ scanf("%d",&T); while(T--){ scanf("%d%d%d",&K,&M,&n); for(i=0;i<n;i++)scanf("%d",&e[i]); for(i=0;i<n;i++)scanf("%d",&p[i]); for(i=0;i<1<<n;i++)for(j=1;j<=100;j++)for(k=0;k<100;k++)f[i][j][k][0]=f[i][j][k][1]=-inf; up(f[0][100][0][0],0); for(i=0;i<1<<n;i++)for(j=100;j;j--)for(k=99;~k;k--)for(o=0;o<2;o++)if(f[i][j][k][o]>=0){ //kill a monster if(j>K)up(f[i][j-K][max(0,k-M)][0],f[i][j][k][o]+1); //drink if(!o)for(t=0;t<n;t++)if(!(i>>t&1)){ if(k+p[t]<100)up(f[i|(1<<t)][min(100,j+e[t])][k+p[t]][1],f[i][j][k][o]); } } ans=0; for(i=0;i<1<<n;i++)for(j=1;j<=100;j++)for(k=0;k<100;k++){ up(ans,f[i][j][k][0]); up(ans,f[i][j][k][1]); } printf("%d\n",ans); } }
I. Sky Tax
根据根与询问点的位置关系分类讨论即可。
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; typedef long long ll; const int N=1000010,M=2000010; int T,C,n,m,root,x,y,i,g[N],v[N<<1],nxt[N<<1],ed; int st[N],en[N],size[N],dfn; int son[N],top[N],d[N],f[N]; inline void add(int x,int y){ v[++ed]=y;nxt[ed]=g[x];g[x]=ed; } void dfs(int x){ size[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x,d[v[i]]=d[x]+1; dfs(v[i]); size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ st[x]=++dfn;top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]); en[x]=dfn; } inline int lca2(int x,int y){ int t; while(top[x]!=top[y])t=top[y],y=f[top[y]]; return x==y?t:son[x]; } inline int ask(int x){ if(root==x)return n; if(st[x]>st[root]||en[x]<en[root])return size[x]; return n-size[lca2(x,root)]; } void check(int x){ if(x<1||x>n)exit(0); } int main(){ scanf("%d",&T); for(C=1;C<=T;C++){ printf("Case #%d:\n",C); scanf("%d%d%d",&n,&m,&root); check(root); for(ed=dfn=0,i=1;i<=n;i++)g[i]=0; for(i=1;i<=n;i++)size[i]=son[i]=top[i]=f[i]=d[i]=0; for(i=1;i<n;i++){ //x=i+1;y=rand()%i scanf("%d%d",&x,&y); check(x); check(y); add(x,y),add(y,x); } dfs(1); dfs2(1,1); while(m--){ scanf("%d%d",&x,&y); check(y); if(x==0)root=y; else{ printf("%d\n",ask(y)); } } } }
J. Printing Press
留坑。
K. Expected Number of Connected Components
留坑。
L. Coordinates
建树DFS即可确定出每个点的坐标。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=100010,M=2000010; const ll inf=1LL<<60; int n,m,i,f[N],g[N],v[M],wx[M],wy[M],nxt[M],ed; ll minx,miny,basex[N],basey[N],xx[N],yy[N]; inline void add(int x,int y,int dx,int dy){ v[++ed]=y; wx[ed]=dx; wy[ed]=dy; nxt[ed]=g[x]; g[x]=ed; } void dfs(int x,int y,ll X,ll Y){ if(f[x])return; f[x]=y; xx[x]=X; yy[x]=Y; minx=min(minx,X); miny=min(miny,Y); for(int i=g[x];i;i=nxt[i])dfs(v[i],y,X+wx[i],Y+wy[i]); } int main(){ scanf("%d%d",&n,&m); while(m--){ int x,y,dx,dy; scanf("%d%d%d%d",&x,&y,&dx,&dy); add(x,y,dx,dy); add(y,x,-dx,-dy); } for(i=1;i<=n;i++)if(!f[i]){ minx=inf; miny=inf; dfs(i,i,0,0); basex[i]=-minx; basey[i]=-miny; } for(i=1;i<=n;i++)printf("%I64d %I64d\n",xx[i]+basex[f[i]],yy[i]+basey[f[i]]); }
总结:
- I题发现模板有误。