SDOI2016 Round1 题解
BZOJ4513 储能表
数位DP,f[i][2][2][2]表示前i位,是否卡n的上界,是否卡m的上界,是否卡k的下界,枚举每一维的下一位直接转移。
#include<cstdio> #include<cstring> typedef unsigned u32; typedef long long ll; ll x,y,z; int p,q; u32 f[61][2][2][2][2]; int main(){ scanf("%d",&q); while(q--){ scanf("%lld%lld%lld%d",&x,&y,&z,&p); memset(f,0,sizeof f); f[60][1][1][1][0]=1; for(int i=59;~i;--i) for(int j=1;~j;--j) for(int k=1;~k;--k) for(int l=1;~l;--l) for(int a=j?x>>i&1:1;~a;--a) for(int b=k?y>>i&1:1;~b;--b) if(!l||(z>>i&1)<=(a^b)){ u32*s=f[i][j&(x>>i&1)==a][k&(y>>i&1)==b][l&(z>>i&1)==(a^b)],*t=f[i+1][j][k][l]; (s[0]+=t[0])%=p,(s[1]+=t[1]*2+(a^b)*t[0])%=p; } printf("%lld\n",(1[****f]-z%p******f%p+p)%p); } }
BZOJ4514 数字配对
若$j$是$i$的倍数且$\Omega(j)-1=\Omega(i)$则$i$,$j$可以配对,其中$\Omega(i)$表示$i$的质因子指数和。按$\Omega(i)$的奇偶性建二分图,跑费用流。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll inf=-1e18; const int N=205; void eq1(int&a,int b){a=b<a?b:a;} struct edge{ int v,c;ll w;edge*s; }e[N*N]; edge*p=e,*h[N]; void ins(int u,int v,ll w,int c){ edge s={v,c,w,h[u]}; edge t={u,0,-w,h[v]}; *(h[u]=p++)=s; *(h[v]=p++)=t; } int n,s,t,q[N*N]; typedef int arr[N]; arr a,b,c,d,f,z; ll r[N]; int dfs(int u,int c){ if(u==t)return c; int f=0; for(edge*i=h[u];i;i=i->s) if(r[u]-i->w==r[i->v]&&d[u]-1==d[i->v]&&i->c){ int j=dfs(i->v,min(c-f,i->c)); i->c-=j,e[i-e^1].c+=j,f+=j; if(f==c)break; } if(!f)d[u]=-1; return f; } int spfa(){ int f=0; ll c=0; while(1){ fill(r+s,r+t,inf); q[0]=t; for(int a=0,b=0;a<=b;++a){ int u=q[a]; z[u]=0; for(edge*i=h[u];i;i=i->s) if(r[i->v]<r[u]-i->w&&e[i-e^1].c){ r[i->v]=r[u]-i->w; d[i->v]=d[u]+1; if(!z[i->v]++)q[++b]=i->v; } } if(r[s]==inf)return f; while(1){ int j=dfs(s,1e9); if(!j)break; if(r[s]<0&&c/-r[s]<=j){ f+=c/-r[s],c%=-r[s]; break; } f+=j,c+=j*r[s]; } } } int main(){ scanf("%d",&n),t=n+1; for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<=n;++i) scanf("%d",b+i); for(int i=1;i<=n;++i) scanf("%d",c+i); for(int i=1;i<=n;++i){ int j=a[i]; for(int d=2;d*d<=j;++d) for(;j%d==0;j/=d)++f[i]; f[i]+=j!=1; } for(int i=1;i<=n;++i) if(f[i]&1){ ins(s,i,0,b[i]); for(int j=1;j<=n;++j) if(abs(f[i]-f[j])==1&&max(a[i],a[j])%min(a[i],a[j])==0) ins(i,j,(ll)c[i]*c[j],1e9); }else ins(i,t,0,b[i]); printf("%d\n",spfa()); }
BZOJ4515 游戏
哪个SB把李超线段树出到树上的……修改拆成两条链,设$t$是$s$的祖先,那么$x$上的数字为$a(d_s-d_x)+b$,所以可以统一插入斜率为$-a$,截距为$ad_s+b$的线段。
#include<bits/stdc++.h> #define I (i+j+2>>1) #define J (i+j>>1) #define P (k<<1) #define S (k<<1^1) using namespace std; template<class T> void eq1(T&a,T b){a=b<a?b:a;} typedef long long ll; const int N=1e5+5; int n,n2; typedef int arr[N]; arr d1,f1,f2,f3,f4,f5,f6; ll d2[N],z[N*4]; struct tag{ int s;ll t; ll operator()(int i){ return s*d2[f6[i]]+t; } }c[N*4]; void ins(tag f,int s,int t,int i,int j,int k){ if(s<=i&&j<=t){ if(i==j){ if(f(i)<c[k](i))c[k]=f; }else if(f(i)<c[k](i)) if(f(j)<c[k](j))c[k]=f; else if(f(J)>c[k](J))ins(f,s,t,i,J,P); else ins(c[k],s,t,I,j,S),c[k]=f; else if(f(j)<c[k](j)) if(f(I)>c[k](I))ins(f,s,t,I,j,S); else ins(c[k],s,t,i,J,P),c[k]=f; eq1(z[k],c[k](i)); eq1(z[k],c[k](j)); }else{ if(s<I)ins(f,s,t,i,J,P); if(t>J)ins(f,s,t,I,j,S); eq1(z[k],z[P]); eq1(z[k],z[S]); } } ll ask(int s,int t,int i,int j,int k){ if(s==i&&j==t)return z[k]; ll y=min(c[k](s),c[k](t)); if(t<I)return min(y,ask(s,t,i,J,P)); if(s>J)return min(y,ask(s,t,I,j,S)); return min(y,min(ask(s,J,i,J,P),ask(I,t,I,j,S))); } struct edge{ int v,w;edge*s; }e[N*2]; edge*x=e,*h[N]; void ins(int u,int v,int w){ edge s={v,w,h[u]}; *(h[u]=x++)=s; } void dfs1(int u){ f1[u]=1; for(edge*i=h[u];i;i=i->s) if(i->v!=f2[u]){ f2[i->v]=u,d1[i->v]=d1[u]+1,d2[i->v]=d2[u]+i->w; dfs1(i->v); f1[u]+=f1[i->v]; if(f1[i->v]>f1[f3[u]]) f3[u]=i->v; } } void dfs2(int u,int v){ f4[f6[f5[u]=++n2]=u]=v; if(f3[u])dfs2(f3[u],v); for(edge*i=h[u];i;i=i->s) if(i->v!=f2[u]&&i->v!=f3[u]) dfs2(i->v,i->v); } int lca(int s,int t){ while(f4[s]!=f4[t]){ if(d1[f4[s]]<d1[f4[t]])swap(s,t); s=f2[f4[s]]; } return d1[s]<d1[t]?s:t; } void ins(tag f,int s,int t){ while(f4[s]!=f4[t]){ ins(f,f5[f4[s]],f5[s],1,n,1); s=f2[f4[s]]; } ins(f,f5[t],f5[s],1,n,1); } int main(){ int m,w,u,v,s,t; scanf("%d%d",&n,&m); for(int i=2;i<=n;++i){ scanf("%d%d%d",&u,&v,&w); ins(u,v,w),ins(v,u,w); } dfs1(1),dfs2(1,1); for(int i=1;i<N*4;++i) c[i].t=z[i]=123456789123456789; while(m--){ scanf("%d%d%d",&w,&s,&t); if(w==1){ scanf("%d%d",&u,&v); int l=lca(s,t); tag f1={-u,u*d2[s]+v},f2={u,u*d2[s]-u*d2[l]*2+v}; ins(f1,s,l); ins(f2,t,l); }else{ ll y=1e18; while(f4[s]!=f4[t]){ if(d1[f4[s]]<d1[f4[t]]) swap(s,t); y=min(y,ask(f5[f4[s]],f5[s],1,n,1)); s=f2[f4[s]]; } if(d1[s]<d1[t])swap(s,t); y=min(y,ask(f5[t],f5[s],1,n,1)); printf("%lld\n",y); } } }
BZOJ4517 排列计数
裸的错排……
#include<cstdio> typedef long long ll; const int p=1e9+7; const int N=1e6+5; int q,n,m; ll f1[N],f2[N],f3[N],f4[N]; int main(){ f1[1]=f2[0]=f3[0]=f4[0]=1; for(int i=2;i<N;++i) f1[i]=f1[p%i]*(p-p/i)%p,f4[i]=(i-1)*(f4[i-1]+f4[i-2])%p; for(int i=1;i<N;++i) f2[i]=f2[i-1]*i%p,f3[i]=f3[i-1]*f1[i]%p; scanf("%d",&q); while(q--){ scanf("%d%d",&n,&m); printf("%d\n",f2[n]*f3[m]%p*f3[n-m]%p*f4[n-m]%p); } }
BZOJ4518 征途
斜率优化,设$s$为前缀和,容易推出$f_j+(m+1)s_j^2=2ms_is_j+f_i-(m-1)s_i^2$,横坐标和斜率都单调,单调队列维护即可。
#include<cstdio> typedef long long ll; const int N=3005; int n,m,a,b,q[N]; ll s[N],f[N],g[N]; double cal1(int i,int j){ return(f[i]-f[j]+(m+1)*(s[i]*s[i]-s[j]*s[j]))/1./(s[i]-s[j]); } ll cal2(int j,int i){ return f[j]+(m-1)*s[i]*s[i]+(m+1)*s[j]*s[j]-m*s[i]*s[j]*2; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%lld",s+i),s[i]+=s[i-1],f[i]=1e18; for(int z=1;z<=m;++z){ q[a=b=0]=z-1; for(int i=z;i<=n;++i){ while(a<b&&cal2(q[a],i)>cal2(q[a+1],i)) ++a; g[i]=cal2(q[a],i); while(a<b&&cal1(q[b],i)<cal1(q[b-1],i)) --b; q[++b]=i; } for(int i=z;i<=n;++i) f[i]=g[i]; } printf("%lld\n",f[n]); }