CQOI2017 部分题解
部分题解是指没写那道算几。
BZOJ上目前没有day2的题面D2T2的图。
BZOJ4813 小Q的棋盘
显然可以$O(n^2)$DP,然而可以$O(n)$贪心:只有一条从根出发的一条链上的边可以只经过一次,其他边如果被经过则必须经过两次,且次数没用完时一定可以用来经过没被经过的边,那么只用枚举那条链的端点。
感觉和SCOI D1T1神似:$O(n^2)$树形DP显然,然而可以$O(n)$贪心。只不过SCOI DP过不了
#include<stdio.h> struct node; typedef struct node edge; struct node{ int v; edge*s; }e[198]; edge*l=e,*h[100]; void ins(int u,int v){ edge s={v,h[u]}; *(h[u]=l++)=s; } int n,m,u,v,a,d[100]; void dfs(int u,int j){ if(m>=d[u]){ if(a<d[u]+m+2>>1) a=d[u]+m+2>>1; for(edge*i=h[u];i;i=i->s) if(i->v!=j) d[i->v]=d[u]+1,dfs(i->v,u); } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<n;++i) scanf("%d%d",&u,&v),ins(u,v),ins(v,u); dfs(0,0); printf("%d\n",a<n?a:n); }
BZOJ4815 小Q的表格
设$\gcd(a,b)=d$,则$f(a,b)=\frac{ab}{d^2}f(d,d)$。
\begin{align*}&\sum_{d=1}^nf(d,d)\sum_{i=1}^{\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\left\lfloor\frac nd\right\rfloor}ij[(i,j)=1]\\=&\sum_{d=1}^nf(d,d)\sum_{i=1}^{\left\lfloor\frac nd\right\rfloor}i^2\varphi(i)\end{align*}
分块维护一下$f(d,d)$就好了,复杂度$O(n+m\sqrt{n})$。
#include<cstdio> #include<cmath> typedef unsigned long long ll; const int p=1e9+7; const int N=4e6+5; int gcd(int a,int b){ return b?gcd(b,a%b):a; } inline int foo(int x){ return x<p?x:x-p; } inline int bar(int x){ return x<0?x+p:x; } inline void inc(int&x,int y){ x=foo(x+y); } typedef int arr[N]; int m,n,nb,bs,np; arr b,c1,c2,c3,c4,f,q; inline int ask(int x){ return x>bs?foo(c3[x]+c4[b[x]]):c3[x]; } void cov(int x,int y){ int d=bar(y-bar(ask(x)-ask(x-1))); for(int i=x;i<=c2[b[x]];++i) inc(c3[i],d); for(int i=b[x]+1;i<=nb-1;++i) inc(c4[i],d); } int main(){ scanf("%d%d",&m,&n); bs=sqrt(n+.5)*3/2; for(int i=1;i<=n;++i) b[i]=(i-1)/bs; nb=b[n]+1; for(int i=1;i<=n;++i) c2[b[i]]=i; for(int i=n;i>=1;--i) c1[b[i]]=i; for(int i=1;i<=n;++i){ c3[i]=(ll)i*i%p; inc(c4[b[i]+1],c3[i]); if(i!=c1[b[i]]) inc(c3[i],c3[i-1]); } for(int i=1;i<nb;++i) inc(c4[i],c4[i-1]); f[1]=1; for(int i=2;i<=n;++i){ if(!f[i]){ f[i]=(ll)i*i%p*(i-1)%p; q[np++]=i; } for(int*j=q;;++j){ if(i**j>n)break; if(i%*j==0){ f[i**j]=(ll)*j**j**j%p*f[i]%p; break; } f[i**j]=(ll)f[i]*f[*j]%p; } } for(int i=2;i<=n;++i) inc(f[i],f[i-1]); while(m--){ int x,y; ll z; scanf("%d%d%lld%d",&x,&y,&z,&n); int d=gcd(x,y); cov(d,z/(x/d)/(y/d)%p); int i=1,s=0; while(i<=n){ int j=n/(n/i); s=(s+(ll)bar(f[j]-f[i-1])*ask(n/i))%p; i=j+1; } printf("%d\n",s); } }
BZOJ4822 老C的任务
签到题。
和SCOI D2T1神似:NOIP难度以下签到题。只不过SCOI有人被卡常数T了5分
#include<bits/stdc++.h> #define lb lower_bound #define ub upper_bound #define pb push_back using namespace std; typedef long long ll; const int N=1e5+5; struct node{ node*i,*j; ll s; }e[N*20]; node*l=e,*r[N]; void ins(int z,int y,node**o){ for(int i=16;~i;--i){ *++l=**o,*o=l; if(y>>i&1)o=&(*o)->j; else (*o)->s+=z,o=&(*o)->i; } } ll ask(int y,node*s,node*t){ ll z=0; for(int i=16;~i;--i) if(~y>>i&1)s=s->i,t=t->i; else z+=t->s-s->s,s=s->j,t=t->j; return z; } int n,m,s,t,u,v,x[N],y[N],z[N],a[N],b[N]; vector<int>c[N]; int main(){ scanf("%d%d",&n,&m); for(int i=0;i<n;++i) scanf("%d%d%d",x+i,y+i,z+i),a[i]=x[i],b[i]=y[i]; sort(a,a+n); sort(b,b+n); for(int i=0;i<n;++i) c[lb(a,a+n,x[i])-a+1].pb(i),y[i]=lb(b,b+n,y[i])-b+1; *(*r=e)=(node){e,e}; for(int i=1;i<=n;++i){ r[i]=r[i-1]; for(int j=0;j<c[i].size();++j){ int k=c[i][j]; ins(z[k],y[k],r+i); } } while(m--){ scanf("%d%d%d%d",&s,&t,&u,&v); s=lb(a,a+n,s)-a+1; t=lb(b,b+n,t)-b+1; u=ub(a,a+n,u)-a; v=ub(b,b+n,v)-b; printf("%lld\n",ask(v+1,r[s-1],r[u])-ask(t,r[s-1],r[u])); } }
BZOJ4823 老C的方块
设特殊边两边的方块为灰色,这两个方块的其他相邻方块分别为黑色和白色,可以将整个棋盘染色。当两个灰色方块同时存在时,相邻的其他方块只能有一种颜色,那么容易建立最小割模型,S向所有黑色方块连边,所有白色方块向T连边,容量为权值;灰色方块与相邻的黑色或白色方块连边,容量为无穷大;两个灰色方块之间连边,容量为两个方块权值的较小值。
#include<bits/stdc++.h> using namespace std; const int dx[]={-1,1,0,0}; const int dy[]={0,0,-1,1}; const int N=1e5+5; const int inf=1061109567; struct edge{ int v,c; edge*s; }e[N*8]; edge*l=e,*h1[N],*h2[N]; void ins(int u,int v,int c){ edge a={v,c,h1[u]}; edge b={u,0,h1[v]}; *(h1[u]=l++)=a; *(h1[v]=l++)=b; } int s1,s2,d[N],q[N]; int dfs(int u,int c){ if(u==s2)return c; int f=0; for(edge*&i=h2[u];i;i=i->s) if(d[u]+1==d[i->v]&&i->c){ int v=dfs(i->v,min(c-f,i->c)); i->c-=v; e[i-e^1].c+=v; f+=v; if(f==c)break; } if(!f)d[u]=-1; return f; } int bfs(){ int f=0; for(;;f+=dfs(s1,inf)){ fill(d,d+s2+1,-1); d[q[0]=s1]=0; for(int a=0,b=0;a<=b;++a){ int u=q[a]; for(edge*i=h2[u]=h1[u];i;i=i->s) if(!~d[i->v]&&i->c) d[q[++b]=i->v]=d[u]+1; } if(!~d[s2])return f; } } int jud(int x,int y){ int a=x&1,b=y&3; return a&&!b||!a&&b==1?0:a&&b==3||!a&&b==2?1:a&&b==1||!a&&!b?2:3; } map<int,int>f[N]; int n,x[N],y[N],w[N]; int main(){ scanf("%*d%*d%d",&n),s1=n,s2=n+1; for(int i=0;i<n;++i){ scanf("%d%d%d",y+i,x+i,w+i); f[x[i]][y[i]]=i; } for(int i=0;i<n;++i){ int x=::x[i],y=::y[i]; if(!jud(x,y)) ins(s1,i,w[i]); else if(jud(x,y)==1) ins(i,s2,w[i]); else for(int j=0;j<4;++j){ int nx=x+dx[j],ny=y+dy[j]; int z=jud(nx,ny); map<int,int>::iterator k=f[nx].find(ny); if(k!=f[nx].end()) if(!z) ins(k->second,i,inf); else if(z==1) ins(i,k->second,inf); else if(z==3) ins(i,k->second,min(w[i],w[k->second])); } } printf("%d\n",bfs()); }
BZOJ4824 老C的键盘
把BZOJ3167粘过来改改就好了。
#include<algorithm> #include<cstdio> using namespace std; typedef unsigned long long ll; const int p=1e9+7; const int N=100; ll f[N][N],g[N],c[N][N]; int n,e[N][N],r[N]; void dfs(int u,int l){ r[u]=f[u][0]=1; for(int v=0;v<n;++v) if(e[u][v]&&v!=l){ dfs(v,u); fill(g,g+n,0); for(int i=0;i<r[u];++i) if(~e[u][v]) for(int j=1;j<=r[v];++j) (g[i+j]+=f[u][i]*f[v][j-1]%p*c[i+j][j]%p*c[r[u]+r[v]-i-j-1][r[v]-j])%=p; else for(int j=0;j<r[v];++j) (g[i+j]+=f[u][i]*f[v][j]%p*c[i+j][j]%p*c[r[u]+r[v]-i-j-1][r[v]-j])%=p; copy(g,g+n,f[u]); r[u]+=r[v]; } if(~e[u][l]) for(int i=r[u]-2;~i;--i) (f[u][i]+=f[u][i+1])%=p; else for(int i=1;i<r[u];++i) (f[u][i]+=f[u][i-1])%=p; } char w[N]; int main(){ for(int i=0;i<N;++i){ c[i][0]=1; for(int j=1;j<N;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%p; } scanf("%d%s",&n,w); for(int i=1;i<n;++i){ int u=(i+1)/2-1; if(w[i-1]=='<') e[i][u]=1,e[u][i]=-1; else e[u][i]=1,e[i][u]=-1; } dfs(0,0); printf("%d\n",f[0][0]); }