hzwer出的BZOJ大赛,ACM赛制,AC:4/5,D题看了题解才发现把最小值看成最大值想了快两个小时。
A.满汉全席
题目大意:n个菜,每种菜可以做成两种菜式中的一种,m个厨师,每个厨师选出两种菜各一种菜式,问是否有方案满足每种菜选一种做,每个厨师选的至少有一种被做到。(n<=100,m<=1000,T组询问,T<=50)
思路:2-sat裸题,每个厨师相当于一个或的关系,复杂度O(T(n+m))。
#include<cstdio> #include<cstring> 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 200 #define ME 2000 struct edge{int nx,t;}e[ME+5]; int n,h[MN+5],en,d[MN+5],l[MN+5],cnt,z[MN+5],inz[MN+5],zn,bn,b[MN+5]; inline void ins(int x,int y){e[++en]=(edge){h[x],y};h[x]=en;} int get() { char c; do c=getchar();while(c!='m'&&c!='h'); return (c=='m')*n+read(); } void tj(int x) { d[x]=l[x]=++cnt;inz[z[zn++]=x]=1; for(int i=h[x];i;i=e[i].nx) { if(!d[e[i].t])tj(e[i].t); if(inz[e[i].t]&&l[e[i].t]<l[x])l[x]=l[e[i].t]; } if(d[x]==l[x])for(++bn;z[zn]!=x;inz[z[zn]]=0)b[z[--zn]]=bn; } int main() { int T=read(),m,i,j;char c; while(T--) { n=read();m=read(); memset(h,en=0,sizeof(h)); while(m--)i=get(),j=get(),ins((i+n-1)%(n<<1)+1,j),ins((j+n-1)%(n<<1)+1,i); memset(d,cnt=0,sizeof(d));memset(l,0,sizeof(l)); memset(z,zn=0,sizeof(z));memset(inz,0,sizeof(inz));memset(b,bn=0,sizeof(b)); for(i=1;i<=n<<1;++i)if(!d[i])tj(i); for(i=1;i<=n;++i)if(b[i]==b[i+n])break; puts(i>n?"GOOD":"BAD"); } }
B.Frozen Nova 冷冻波
题目大意:有n个巫妖和m个小精灵,表示成平面上的点,k棵树,表示成平面上的圆,一个巫妖可以消灭一个小精灵当且仅当他与这个小精灵的距离不超过这个巫妖的攻击半径且巫妖与小精灵连线与树无交点,每个巫妖隔ti秒可以消灭一个小精灵,问多久能消灭所有小精灵,无解输出-1。(n,m,k<=200)
思路:预处理出第i个巫妖能否消灭第j个小精灵,攻击半径直接判,然后枚举树,即判断一条线段与圆心距离是否超过半径,用点积叉积分类计算即可。最后二分答案,网络流check,复杂度O(nmk+logAns*最大流(n+m,nm))。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define lb long double inline int read() { int x,f=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')f=0; for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return f?x:-x; } #define MN 200 #define eps 1e-9 #define MV 400 #define S MV+1 #define T MV+2 #define ME 50000 #define INF 0x7FFFFFFF int dcmp(lb x){return fabs(x)<eps?0:x<0?-1:1;} struct point{lb x,y;}p1[MN+5],p2[MN+5],p3[MN+5]; point operator-(point a,point b){return (point){a.x-b.x,a.y-b.y};} lb operator*(point a,point b){return a.x*b.y-a.y*b.x;} lb dot(point a,point b){return a.x*b.x+a.y*b.y;} lb length(point a){return sqrt(dot(a,a));} lb dis(point p,point a,point b) { if(dot(p-a,b-a)<0)return length(p-a); if(dot(p-b,a-b)<0)return length(p-b); return fabs((p-a)*(b-a)/length(b-a)); } struct edge{int nx,t,w;}e[ME*2+5]; int h[MV+5],en,t[MN+5],r1[MN+5],r3[MN+5],g[MN+5][MN+5],d[MV+5],q[MV+5],qn,c[MV+5]; inline void ins(int x,int y,int w) { e[++en]=(edge){h[x],y,w};h[x]=en; e[++en]=(edge){h[y],x,0};h[y]=en; } bool bfs() { int i,j; memset(d,0,sizeof(d)); for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx) if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1; return d[T]; } int dfs(int x,int r) { if(x==T)return r; int k,u=0; for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1) { k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w); u+=k;e[i].w-=k;e[i^1].w+=k; if(u==r)return r; } return d[x]=0,u; } int main() { int n,m,k,i,j,l,r,mid,ans=-1,cnt; scanf("%d%d%d",&n,&m,&k); for(i=1;i<=n;++i)p1[i].x=read(),p1[i].y=read(),r1[i]=read(),t[i]=read(); for(i=1;i<=m;++i)p2[i].x=read(),p2[i].y=read(); for(i=1;i<=k;++i)p3[i].x=read(),p3[i].y=read(),r3[i]=read(); for(i=1;i<=n;++i)for(j=1;j<=m;++j) { if(dcmp(length(p1[i]-p2[j])-r1[i])>0)continue; for(l=1;l<=k;++l)if(dcmp(dis(p3[l],p1[i],p2[j])-r3[l])<=0)break; g[i][j]=l>k; } for(l=0,r=4000000;l<=r;) { mid=l+r>>1; memset(h,0,sizeof(h));en=1; for(i=1;i<=n;++i)ins(S,i,mid/t[i]+1); for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(g[i][j])ins(i,j+n,1); for(i=1;i<=m;++i)ins(i+n,T,1); for(cnt=0;bfs();)cnt+=dfs(S,INF); if(cnt<m)l=mid+1;else ans=mid,r=mid-1; } printf("%d",ans); }
C.亚瑟王
题目大意:n张卡牌,进行r次游戏,每次从左到右处理,对于第i张卡牌,若这张卡牌没用过,则有pi几率使用这张牌获得di的权值并结束这轮游戏,求最后获得的权值期望,多组询问。(询问组数<=444,n<=220,r<=132)
思路:用f[i][j]表示到第i张牌还有j场游戏没结束时的权值期望,c[i][j]表示到第i张还有j场没结束的概率,t[i][j]表示第i张卡有j场经过他后,被使用的概率,则t[i][j]=t[i][j-1]+(1-t[i][j-1])*pi,c[i][j]=c[i-1][j+1]*t[i][j+1]+c[i-1][j]*(1-t[i][j]),f[i][j]=(f[i-1][j+1]+di*c[i-1][j+1])*t[i][j+1]+f[i-1][j]*(1-t[i][j])。
#include<cstdio> #include<cstring> #define MN 220 #define MR 132 double f[MN+5][MR+5],c[MN+5][MR+5]; int main() { int T,n,m,i,j,k,z;double p,s,ans; for(scanf("%d",&T);T--;) { scanf("%d%d",&n,&m); memset(f,0,sizeof(f));memset(c,0,sizeof(c));c[0][m]=1; for(i=1;i<=n;++i) { scanf("%lf%d",&p,&z); for(j=s=0;j<=m;++j,s+=(1-s)*p) { if(j)f[i][j-1]+=(f[i-1][j]+z*c[i-1][j])*s,c[i][j-1]+=c[i-1][j]*s; f[i][j]+=f[i-1][j]*(1-s);c[i][j]+=c[i-1][j]*(1-s); } } for(i=ans=0;i<=m;++i)ans+=f[n][i]; printf("%.10lf ",ans); } }
D.切糕
题目大意:给出p*q*r的长方体权值点阵v,构造一个函数f(x,y),使得若|x1-x2|+|y1-y2|<=1,则|f(x1,y1)-f(x2,y2)|<=d,求v[x][y][f(x,y)]和的最小值。(p,q,r<=40,d<=r)
思路:考虑最小割模型,用p(i,j,k)代表f(i,j)=k,我们让S向每个p(i,j,0)连边,p(i,j,r)向T连边,p(i,j,k-1)向p(i,j,k)连边(该边权值为v[i][j][k],其他边权值为INF),就可以保证f(x,y)只会取一个,考虑|f(x1,y1)-f(x2,y2)|<=d的限制,事实上与f(x1,y1)-f(x2,y2)<=d相同,我们让p(x1,y1,k)向p(x2,y2,k-d)连边即可解决(可以画图验证),复杂度O(能过)。
#include<cstdio> #include<cstring> 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 40 #define MV 65600 #define ME 329600 #define S MV+1 #define T MV+2 #define INF 0x7FFFFFFF const int o[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; struct edge{int nx,t,w;}e[ME*2+5]; int h[MV+5],en=1,v[MN+5][MN+5][MN+5],d[MV+5],q[MV+5],qn,c[MV+5]; inline void ins(int x,int y,int w) { e[++en]=(edge){h[x],y,w};h[x]=en; e[++en]=(edge){h[y],x,0};h[y]=en; } int P(int x,int y,int z){return z*MN*MN+x*MN+y;} bool bfs() { int i,j; memset(d,0,sizeof(d)); for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx) if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1; return d[T]; } int dfs(int x,int r) { if(x==T)return r; int k,u=0; for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1) { k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w); u+=k;e[i].w-=k;e[i^1].w+=k; if(u==r)return r; } return d[x]=0,u; } int main() { int p,q,r,d,i,j,k,l,x,y,ans=0; p=read();q=read();r=read();d=read(); for(i=1;i<=r;++i)for(j=0;j<p;++j)for(k=0;k<q;++k)v[j][k][i]=read(); for(i=0;i<p;++i)for(j=0;j<q;++j) { ins(S,P(i,j,0),INF),ins(P(i,j,r),T,INF); for(k=1;k<=r;++k)ins(P(i,j,k-1),P(i,j,k),v[i][j][k]); for(l=0;l<4;++l) { if((x=i+o[l][0])<0||x==p||(y=j+o[l][1])<0||y==q)continue; for(k=d;k<=r;++k)ins(P(i,j,k),P(x,y,k-d),INF); } } while(bfs())ans+=dfs(S,INF); printf("%d",ans); }
E.米特运输
题目大意:一棵n个点的树,点有点权,求最少修改多少个点的点权,使得每个点的各儿子点权相同且和为该点点权。(n<=500,000)
思路:设根节点点权为x,则每个点的点权都能表示为x/fi,其中各个点的fi为定值,不难一遍dfs求出,ai=x/fi移项得x=ai*fi,则最优的x为出现次数最多的ai*fi,我们对每个点计算出ai*fi放入map即可统计答案,fi可能很大,我们随便用个大质数取模下就可以了,O(nlogn)。
#include<cstdio> #include<map> #include<algorithm> using namespace std; 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 500000 #define MOD 1000000007 struct edge{int nx,t;}e[MN*2+5]; int h[MN+5],en,a[MN+5],ans; inline void ins(int x,int y) { e[++en]=(edge){h[x],y};h[x]=en; e[++en]=(edge){h[y],x};h[y]=en; } map<int,int> mp; void dfs(int x,int fa,int p) { int i,cnt=0; ans=max(ans,++mp[1LL*a[x]*p%MOD]); for(i=h[x];i;i=e[i].nx)if(e[i].t!=fa)++cnt; for(i=h[x];i;i=e[i].nx)if(e[i].t!=fa)dfs(e[i].t,x,1LL*p*cnt%MOD); } int main() { int n=read(),i; for(i=1;i<=n;++i)a[i]=read(); for(i=1;i<n;++i)ins(read(),read()); dfs(1,0,1); printf("%d",n-ans); }