bzoj usaco 金组水题题解(2)
续。。。。。TAT这回不到50题编辑器就崩了。。
这里塞40道吧= =
bzoj 1585: [Usaco2009 Mar]Earthquake Damage 2 地震伤害
比较经典的最小割?。。然而一开始还是不会QAQ
和地震伤害1的区别在于这题求的是最少的损坏牧场数目。把牧场拆点,因为要让1和被报告的点不联通,把1归到S集,被报告的点归到T集,就变成求最小割了。
具体建图:
假设点拆成x和x’,x和x‘间连边(就是等下要割的)。被报告的点和1点:容量无穷大(不能割);其他点容量为1。
原图中相连的边就按照二分图正常姿势,出点连到入点,容量无穷大(路没坏)。
最后就是s连1,被报告的点连t,容量都无穷大
dinic大法好。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=3003; 6 const int inf=1023333333; 7 struct zs{ 8 int too,pre,flow; 9 }e[98233]; 10 int last[maxn<<1],dl[maxn<<1],l,r,now,tot=1; 11 short dis[maxn<<1]; 12 bool u[maxn]; 13 int i,j,k,n,m,a,b,s,t,p,ans; 14 15 int ra;char rx; 16 inline int read(){ 17 rx=getchar();ra=0; 18 while(rx<'0'||rx>'9')rx=getchar(); 19 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 20 } 21 inline void insert(int a,int b,int c){ 22 // printf(" %d-->%d %d\n",a,b,c==1?1:233); 23 e[++tot].too=b;e[tot].flow=c;e[tot].pre=last[a];last[a]=tot; 24 e[++tot].too=a;e[tot].flow=0;e[tot].pre=last[b];last[b]=tot; 25 } 26 inline bool bfs(){ 27 memset(dis,255,(t+1)<<1); 28 l=0;r=1;dl[1]=s;dis[s]=0; 29 while(l<r&&dis[t]==-1)for(now=dl[++l],i=last[now];i;i=e[i].pre)if(e[i].flow&&dis[e[i].too]==-1) 30 dis[e[i].too]=dis[now]+1,dl[++r]=e[i].too; 31 // for(i=1;i<=t;i++)printf(" %d %d\n",i,dis[i]); 32 return dis[t]!=-1; 33 } 34 int dfs(int x,int mx){ 35 if(x==t||!mx)return mx; 36 int used=0,i,w; 37 for(i=last[x];i;i=e[i].pre)if(dis[e[i].too]==dis[x]+1&&e[i].flow){ 38 w=dfs(e[i].too,min(mx-used,e[i].flow));if(w){ 39 e[i].flow-=w;e[i^1].flow+=w; 40 used+=w;if(used==mx)return mx; 41 } 42 } 43 dis[x]=-1;return used; 44 } 45 int main(){ 46 n=read();m=read();p=read();s=0;t=n+n+1; 47 for(i=1;i<=m;i++)a=read(),b=read(),insert(a+n,b,inf),insert(b+n,a,inf); 48 for(i=1;i<=p;i++)a=read(),u[a]=1,insert(a,a+n,inf),insert(a+n,t,inf); 49 for(i=2;i<=n;i++)if(!u[i])insert(i,i+n,1);if(!u[1])insert(1,1+n,inf); 50 insert(s,1,inf); 51 while(bfs())ans+=dfs(s,inf); 52 printf("%d\n",ans); 53 return 0; 54 } 55 56 View
bzoj 2200: [Usaco2011 Jan]道路和航线
正解:根据题目,一条航线就是图的一条割边。。 所以先把图中各个联通块单独拿出来,在联通块里跑最短路(这时联通块里没有负权边,就用Dijkstra+堆去求)。再按联通块之间的拓扑顺序把整张图的最短路求出来。
又wa又T一时爽。。一开始是没注意负权边直接上dij,然后是spfa被卡。。。。spfa+slf就可过了= =巧妙的避开了正解(逃。。。
话说spfa好像并不难卡?。。。以后得小心了QAQ
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=25233; 8 const int mx=25233; 9 struct zs{ 10 int too,pre,dis; 11 }e[152333]; 12 int dl[mx+1]; 13 int i,j,k,n,m,m1,s,tot,a,b,c; 14 int dis[maxn],last[maxn]; 15 bool u[maxn]; 16 int ra,fh;char rx; 17 inline int getnext(int x){ 18 x++;if(x>=mx)return 1; 19 return x; 20 } 21 inline int getpre(int x){ 22 x--;if(x<1)return mx-1; 23 return x; 24 } 25 inline int read(){ 26 rx=getchar();ra=0;fh=1; 27 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 28 if(rx=='-')fh=-1,rx=getchar(); 29 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 30 } 31 inline void insert(int a,int b,int c){ 32 e[++tot].too=b;e[tot].dis=c;e[tot].pre=last[a];last[a]=tot; 33 } 34 35 inline void spfa(){ 36 int size=1,i,now,to,l,r;//,tt=0; 37 memset(dis,60,(n+1)<<2); 38 dis[s]=0;dl[1]=s;l=0;r=1; 39 while(size){ 40 l=getnext(l);size--; 41 now=dl[l];u[now]=0; 42 for(i=last[now],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(dis[to]>dis[now]+e[i].dis){ 43 dis[to]=dis[now]+e[i].dis; 44 if(!u[to]) 45 if(size&&dis[to]<dis[dl[getnext(l)]])dl[l]=to,l=getpre(l),size++,u[to]=1; 46 else r=getnext(r),dl[r]=to,size++,u[to]=1; 47 } 48 } 49 } 50 int main(){ 51 n=read();m=read();m1=read();s=read(); 52 for(i=1;i<=m;i++)a=read(),b=read(),c=read(),insert(a,b,c),insert(b,a,c); 53 for(i=1;i<=m1;i++)a=read(),b=read(),c=read(),insert(a,b,c); 54 spfa(); 55 for(i=1;i<=n;i++)if(dis[i]<923333333)printf("%d\n",dis[i]);else puts("NO PATH"); 56 return 0; 57 }
bzoj 1575: [Usaco2009 Jan]气象牛Baric
正常dp。。。先预处理出sum[i][j]表示s集中选了第i天和第j天时,原序列中第i+1~j-1天的误差值之和。pre[i]表示s集中第一个选了i的话,1~i-1天的误差和。aft[i]表示s集中最后一个是第i天的话i+1~n天的误差和。
f[i][j]表示最后选第i天,总共选了j天的第1~i天的最小误差。初始化f[i][1]=pre[i];
f[i][j]=min{f[k][j-1]+sum[k][i]},(1<=k<i,2<=j<=n)。如果min{ f[i][j]+aft[i] }<=E的话就可以了
总复杂度O(n^3)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 using namespace std; 5 const int maxn=102; 6 int i,j,k,n,m,l,r,mid,ans,tmp; 7 int a[maxn],sum[maxn][maxn],aftsum[maxn],befsum[maxn]; 8 int f[maxn][maxn]; 9 int ra;char rx; 10 inline int read(){ 11 rx=getchar();ra=0; 12 while(rx<'0'||rx>'9')rx=getchar(); 13 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 14 } 15 int main(){ 16 n=read();m=read(); 17 for(i=1;i<=n;i++)a[i]=read(); 18 for(i=1;i<n;i++)for(j=i+1;j<=n;j++) 19 for(k=i+1;k<j;k++)sum[i][j]+=abs((a[k]<<1)-a[i]-a[j]); 20 for(i=n;i;i--){ 21 for(j=n;j>i;j--)aftsum[i]+=abs(a[j]-a[i])<<1; 22 for(j=1;j<i;j++)befsum[i]+=abs(a[j]-a[i])<<1; 23 } 24 for(i=1;i<=n;i++)f[i][1]=befsum[i]; 25 ans=102333333; 26 for(j=1;j<=n&&ans>m;j++){ 27 for(i=j;i<=n;i++){ 28 if(j>1) 29 for(f[i][j]=f[1][j-1]+sum[1][i],k=2;k<i;k++)if(f[k][j-1]+sum[k][i]<f[i][j])f[i][j]=f[k][j-1]+sum[k][i]; 30 if(f[i][j]+aftsum[i]<ans)ans=f[i][j]+aftsum[i]; 31 } 32 } 33 printf("%d %d\n",j-1,ans); 34 return 0; 35 }
bzoj 1755: [Usaco2005 qua]Bank Interest
如题。。就是求算存钱若干年后的本息之和
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define d double 5 using namespace std; 6 int i,j,k,n,m,r,y; 7 d ans; 8 int main(){ 9 scanf("%d%d%d",&r,&m,&y); 10 ans=(d)(100+r)/(d)100.0; 11 for(i=2;i<=y;i++)ans=ans*(d)(100+r)/(d)100.0; 12 printf("%d\n",y?(int)(ans*m):m); 13 return 0; 14 }
bzoj 1774: [Usaco2009 Dec]Toll 过路费
floyd。。http://www.cnblogs.com/czllgzmzl/p/5070943.html
bzoj 1775: [Usaco2009 Dec]Vidgame 电视游戏问题
一开始以为是状压。。。结果发现数据范围是给01背包的= =
ans[i]表示指出i元的最多产出,f[i]表示当前游戏平台内,花i元买游戏的最大产出。初始化f[i]=ans[i-P](买游戏得先买平台),(P是当前游戏平台的价格)f[i]就是个01背包。
ans[i]=max(ans[i],f[i])。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 int cost,val; 6 int need,num; 7 int f[100233],g[100233]; 8 int i,j,k,n,m,maxv,tmp,ans; 9 int ra;char rx; 10 inline int read(){ 11 rx=getchar();ra=0; 12 while(rx<'0'||rx>'9')rx=getchar(); 13 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 14 } 15 int main(){ 16 n=read();maxv=read(); 17 for(i=1;i<=n;i++){ 18 need=read();num=read(); 19 memset(g,150,need<<2); 20 for(j=need;j<=maxv;j++)g[j]=f[j-need]; 21 for(j=0;j<num;j++) 22 for(cost=read(),val=read(),k=maxv,tmp=k-cost;tmp>=0;k--,tmp--) 23 if(g[tmp]+val>g[k])g[k]=g[tmp]+val; 24 for(j=maxv;j>=need;j--)if(f[j]<g[j])f[j]=g[j]; 25 } 26 for(i=maxv;i>=0;i--)if(f[i]>ans)ans=f[i]; 27 printf("%d\n",ans); 28 return 0; 29 }
bzoj 1696: [Usaco2007 Feb]Building A New Barn新牛舍
中位数。。好吧是平面上的。设奶牛吃草位置为x[],y[],理想情况下(ansx,ansy)分别是x数组、y数组排序后的中位数。
n为奇数时,最理想的点是唯一的。但如果那个地方已经有奶牛吃草了的话,就尝试下它相邻4个点(这是仅有的可能次优的几个)。
n为偶数时,点(x[n/2],y[n/2])与点(x[n/2+1],y[n/2+1])组成的矩形内的点都是最优的。(x、y已排序)最优点的个数要减去在矩形里吃草的牛的数量。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cstdlib> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=10233; 8 struct zs{ 9 int x,y; 10 }a[maxn]; 11 int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0}; 12 int i,j,k,n,m,maxv,tmp,ans,x,y,nowx,nowy,x1,y1,sum; 13 bool cant; 14 int ra,fh;char rx; 15 inline int read(){ 16 rx=getchar();ra=0;fh=1; 17 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 18 if(rx=='-')rx=getchar(),fh=-1; 19 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 20 } 21 bool cmp1(zs a,zs b){ 22 return a.x<b.x; 23 } 24 bool cmp2(zs a,zs b){ 25 return a.y<b.y; 26 } 27 inline int get(int x,int y){ 28 int ans=0,i; 29 for(i=1;i<=n;i++)ans+=abs(x-a[i].x)+abs(y-a[i].y);return ans; 30 } 31 int main(){ 32 n=read(); 33 for(i=1;i<=n;i++)a[i].x=read(),a[i].y=read(); 34 ans=2023333333; 35 if(n&1){ 36 sort(a+1,a+1+n,cmp1);x=a[(n+1)>>1].x; 37 sort(a+1,a+1+n,cmp2);y=a[(n+1)>>1].y; 38 for(i=1;i<=n;i++)if(a[i].x==x&&a[i].y==y){cant=1;break;} 39 if(!cant)ans=get(x,y),sum=1; 40 else for(i=0;i<4;i++){ 41 nowx=x+xx[i];nowy=y+yy[i]; 42 tmp=get(nowx,nowy);if(tmp<ans)ans=tmp,sum=1;else if(tmp==ans)sum++; 43 } 44 }else{ 45 sort(a+1,a+1+n,cmp1);x=a[n>>1].x;x1=a[n/2+1].x; 46 sort(a+1,a+1+n,cmp2);y=a[n>>1].y;y1=a[n/2+1].y; 47 ans=get(x,y);sum=(x1-x+1)*(y1-y+1); 48 for(i=1;i<=n;i++)if(a[i].x>=x&&a[i].x<=x1&&a[i].y>=y&&a[i].y<=y1)sum--; 49 } 50 printf("%d %d\n",ans,sum); 51 return 0; 52 }
bzoj 1916: [Usaco2010 Open]冲浪
dp。按照拓扑顺序来。。f[i][j]表示从i点到n点,途中j次失误的最大愉悦值,val(i,j)表示i到j这条边的愉悦值。
f[i][j]=min{ max{ f[k][j]+val(i,k) }(没失误),min{ f[k][j-1]+val(i,k) }(失误了= =) },(存在边i-->k)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int maxn=50023; 7 struct zs{ 8 int dis,too,pre; 9 }e[150233],E[150233]; 10 int last[maxn],cd[maxn],las[maxn]; 11 ll f[maxn][11]; 12 int dl[maxn],l,r,now,to; 13 int i,j,k,K,n,m,a,b,c; 14 int ra;char rx; 15 inline int read(){ 16 rx=getchar();ra=0; 17 while(rx<'0'||rx>'9')rx=getchar(); 18 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 19 } 20 inline void ins(int a,int b,int c){ 21 E[i].too=b;E[i].dis=c;E[i].pre=las[a];las[a]=i; 22 } 23 int main(){ 24 n=read();m=read();K=read(); 25 for(i=1;i<=m;i++) 26 a=read(),b=read(),e[i].dis=read(),e[i].too=a,e[i].pre=last[b],last[b]=i,cd[a]++,ins(a,b,e[i].dis); 27 //memset(f,255,sizeof(f)); 28 r=1;dl[1]=n;f[n][0]=0; 29 while(l<r){ 30 now=dl[++l]; 31 for(i=last[now],to=e[i].too;i;i=e[i].pre,to=e[i].too){ 32 cd[to]--;if(!cd[to])dl[++r]=to; 33 if(f[now][0]+e[i].dis>f[to][0])f[to][0]=f[now][0]+e[i].dis; 34 } 35 } 36 // for(i=1;i<=n;i++)printf(" %d %lld\n",dl[i],f[dl[i]][0]); 37 for(i=2,now=dl[i];i<=n;now=dl[++i]){ 38 for(j=las[now],to=E[j].too;j;j=E[j].pre,to=E[j].too) 39 for(k=1;k<=K;k++)if(f[to][k]+E[j].dis>f[now][k])f[now][k]=f[to][k]+E[j].dis; 40 for(j=las[now],to=E[j].too;j;j=E[j].pre,to=E[j].too) 41 for(k=1;k<=K;k++)if(f[to][k-1]+E[j].dis<f[now][k])f[now][k]=f[to][k-1]+E[j].dis; 42 // for(j=0;j<=K;j++)printf(" %d %d: %lld\n",now,j,f[now][j]); 43 } 44 printf("%lld\n",f[1][K]); 45 return 0; 46 }
bzoj 1751: [Usaco2005 qua]Lake Counting
如题。。联通块计数。。注意是八连通= =
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=102; 6 int map[maxn*maxn]; 7 int xx[8]={1,1,1,0,0,-1,-1,-1},yy[8]={-1,0,1,-1,1,-1,0,1}; 8 int fa[maxn*maxn]; 9 int i,j,k,n,m,x,y,nowx,nowy,tmpx,tmpy,tmp,now,ans; 10 int ra;char rx; 11 inline int read(){ 12 rx=getchar();ra=0; 13 while(rx<'0'||rx>'9')rx=getchar(); 14 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 15 } 16 int getfa(int x){ 17 if(fa[x]!=x)fa[x]=getfa(fa[x]); 18 return fa[x]; 19 } 20 int main(){ 21 n=read();m=read(); 22 for(i=1;i<=n;i++)for(j=1;j<=m;j++){ 23 for(rx=getchar();rx!='.'&&rx!='W';rx=getchar()); 24 map[(i-1)*m+j]=(rx=='W')?2:1; 25 fa[(i-1)*m+j]=(i-1)*m+j; 26 } 27 for(i=1;i<=n;i++)for(j=1;j<=m;j++){ 28 tmp=(i-1)*m+j; 29 if(map[tmp]!=2)continue; 30 for(k=0;k<8;k++){ 31 nowx=i+xx[k];nowy=j+yy[k]; 32 if(nowx<1||nowy<1||nowx>n||nowy>m)continue; 33 now=(nowx-1)*m+nowy; 34 if(map[now]!=2)continue; 35 x=getfa(tmp);y=getfa(now); 36 if(x!=y)fa[y]=x; 37 } 38 } 39 for(i=n*m;i;i--)if(map[i]==2&&fa[i]==i)ans++; 40 printf("%d\n",ans); 41 return 0; 42 }
bzoj 1914: [Usaco2010 OPen]Triangle Counting 数三角形
极角排序一下。。http://www.cnblogs.com/czllgzmzl/p/5071114.html
bzoj 1590: [Usaco2008 Dec]Secret Message 秘密信息
好久没写trie了。。。注意在trie树上得记录两个信息:在当前节点结束的信息数量;在当前节点还没结束的信息数量。。
每次查找的答案就是 路径上已经结束的数量+最后那个节点上还没结束的信息数量。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 const int maxn=500023; 6 int map[maxn][4],len,tot,tmp; 7 int i,j,k,n,m,now,ans; 8 int ra;char rx; 9 inline int read(){ 10 rx=getchar();ra=0; 11 while(rx<'0'||rx>'9')rx=getchar(); 12 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 13 } 14 int main(){ 15 n=read();m=read(); 16 for(i=1;i<=n;map[now][3]++,i++) 17 for(now=0,len=read();len;len--){ 18 for(rx=getchar();rx!='0'&&rx!='1';rx=getchar());tmp=rx-48; 19 if(!map[now][tmp])map[now][tmp]=++tot,now=tot;else now=map[now][tmp]; 20 if(len>1)map[now][2]++; 21 } 22 for(i=1;i<=m;printf("%d\n",ans+map[now][2]),i++) 23 for(now=ans=0,len=read();len;len--){ 24 for(rx=getchar();rx!='0'&&rx!='1';rx=getchar());tmp=rx-48; 25 now=map[now][tmp];ans+=map[now][3]; 26 if(!now){while(--len)for(rx=getchar();rx!='0'&&rx!='1';rx=getchar());break;} 27 } 28 return 0; 29 }
bzoj 1718: [Usaco2006 Jan] Redundant Paths 分离的路径
边双联通分量。。先把边双连通分量缩点,就变成如何使一棵树上两两节点间有多条路可走。。。答案就是(叶子节点数量+1)/2。
求边双连通分量时,记录一下当前点的父亲边,如果low[x]==dfn[x],那么点x的父亲边就是桥。把桥都从原图中去掉,剩下的就是一坨边双联通分量了。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1005; 6 struct zs{ 7 int too,pre; 8 }e[20023]; 9 int last[maxn],tot=1; 10 bool isbridge[20023]; 11 int dfn[maxn],low[maxn],tim; 12 int degree[maxn],bel[maxn],blocknum; 13 int i,j,k,n,m,a,b,ans; 14 15 int ra;char rx; 16 inline int read(){ 17 rx=getchar();ra=0; 18 while(rx<'0'||rx>'9')rx=getchar(); 19 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 20 } 21 void tarjan(int x,int preedge){ 22 int i,to;dfn[x]=low[x]=++tim; 23 for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too)if((i^1)!=preedge) 24 if(!dfn[to])tarjan(to,i),low[x]=low[x]>low[to]?low[to]:low[x]; 25 else low[x]=low[x]>dfn[to]?dfn[to]:low[x]; 26 if(preedge&&low[x]==dfn[x])isbridge[preedge]=isbridge[preedge^1]=1; 27 } 28 inline void insert(int a,int b){ 29 e[++tot].too=b;e[tot].pre=last[a];last[a]=tot; 30 e[++tot].too=a;e[tot].pre=last[b];last[b]=tot; 31 } 32 void rebuild(int x,int num){ 33 bel[x]=num; 34 for(int i=last[x];i;i=e[i].pre)if(!isbridge[i]&&!bel[e[i].too])rebuild(e[i].too,num); 35 } 36 int main(){ 37 n=read();m=read(); 38 for(i=1;i<=m;i++)a=read(),b=read(),insert(a,b); 39 for(i=1;i<=n;i++)if(!dfn[i])tarjan(i,0); 40 for(i=1;i<=n;i++)if(!bel[i])rebuild(i,++blocknum); 41 for(i=2;i<=tot;i+=2)if(isbridge[i])degree[bel[e[i].too]]++,degree[bel[e[i^1].too]]++; 42 for(i=1;i<=blocknum;i++)if(degree[i]==1)ans++; 43 printf("%d\n",(ans+1)>>1); 44 return 0; 45 }
bzoj 1693: [Usaco2007 Demo]Asteroids
盯了半天英文题面。。。结果发现双倍经验。同bzoj1741。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=503; 6 struct zs{ 7 int too,pre; 8 bool flow; 9 }e[22333]; 10 int last[maxn<<1],u[maxn<<1],dl[maxn<<1]; 11 short dis[maxn<<1]; 12 int i,j,k,n,m,tot,s,t,a,b,ans; 13 int ra;char rx; 14 inline int read(){ 15 rx=getchar();ra=0; 16 while(rx<'0'||rx>'9')rx=getchar(); 17 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 18 } 19 inline void insert(int a,int b){ 20 e[++tot].too=b;e[tot].flow=1;e[tot].pre=last[a];last[a]=tot; 21 e[++tot].too=a;e[tot].flow=0;e[tot].pre=last[b];last[b]=tot; 22 } 23 inline bool bfs(){ 24 memset(dis,255,(t+1)<<1); 25 int l=0,r=1,now,i;dl[1]=s;dis[s]=0; 26 while(l<r){ 27 now=dl[++l]; 28 for(i=last[now];i;i=e[i].pre)if(dis[e[i].too]==-1&&e[i].flow) 29 dis[e[i].too]=dis[now]+1,dl[++r]=e[i].too; 30 } 31 return dis[t]!=-1; 32 } 33 int dfs(int x,int mx){ 34 if(x==t)return mx; 35 int i,used=0,w; 36 for(i=last[x];i;i=e[i].pre)if(e[i].flow&&dis[e[i].too]==dis[x]+1){ 37 w=dfs(e[i].too,1);if(w){ 38 e[i].flow=0;;e[i^1].flow=1; 39 used++;if(used==mx)return mx; 40 } 41 } 42 dis[x]=-1;return used; 43 } 44 int main(){tot=1; 45 n=read();m=read();s=0;t=n+n+1; 46 for(i=1;i<=n;i++)insert(s,i),insert(i+n,t); 47 for(i=1;i<=m;i++) 48 a=read(),b=read(),insert(a,b+n); 49 while(bfs())ans+=dfs(s,2333333); 50 printf("%d\n",ans); 51 return 0; 52 }
bzoj 1716: [Usaco2006 Dec]The Fewest Coins 找零钱
搜题解的话应该连题目名字一起搜。。因为也是poj3260。
店家找钱一定不超过v_max^2。。。因为这就意味着John多给店主硬币超过了v_max个。。想要让这些硬币无法用v_max的硬币替代掉一部分,则它们不同组合的价值和对v_max取模后都各不相同。。。。总之根据抽屉原理这是不可能的。。。
所以对店家跑个无限背包,对john跑个有限背包就行了。
UPD:。。。。。。。。。。。。。。。。。。。论傻逼不好好想题就跑去看题解的危害性。
根据KPM代码可得。。店家找钱一定不超过v_max。。。其实这也是很显然的?(捂脸。。然而为何我想不到)。。找钱超过v_max肯定有硬币是脑残多给的啊。。。
不过更加不科学的是。。。我数组开大反而快了不少。。。。而且是多次试验确定不是测评姬看脸= =。。。。导演这和说好的不一样。。。
数组开2w+就#1了。。。开1w+就慢了快一倍TAT。。。感人肺腑
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=102<<4; 7 const int inf=800233333; 8 struct zs{ 9 int c,v; 10 }a[maxn]; 11 int f[25233],owner[15233]; 12 int c[102],v[102]; 13 int i,j,k,n,m,ans,mx,N; 14 int ra;char rx; 15 inline int read(){ 16 rx=getchar();ra=0; 17 while(rx<'0'||rx>'9')rx=getchar(); 18 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 19 } 20 bool cmp(zs a,zs b){ 21 return a.v<b.v; 22 } 23 int main(){ 24 n=read();m=read(); 25 for(i=1;i<=n;i++)v[i]=read(),mx=max(mx,v[i]); 26 for(i=1;i<=n;i++)c[i]=read(); 27 // mx*=mx; 28 for(i=1;i<=n;i++){ 29 for(j=1;j<=c[i];j<<=1)a[++N].c=j,a[N].v=j*v[i],c[i]-=j; 30 if(c[i])j=c[i],a[++N].c=j,a[N].v=j*v[i]; 31 } 32 sort(a+1,a+1+N,cmp); 33 while(N&&a[N].v>mx+m)N--; 34 // for(i=1;i<=N;i++)printf(" %d %d\n",a[i].c,a[i].v); 35 memset(owner,50,(mx+1)<<2); 36 memset(f,50,(mx+1+m)<<2);owner[0]=f[0]=0; 37 for(i=1;i<=n;i++)for(j=v[i];j<=mx;j++)if(owner[j]>owner[j-v[i]]+1)owner[j]=owner[j-v[i]]+1; 38 // for(i=1;i<=20;i++)printf(" %d %d\n",i,owner[i]); 39 for(i=1;i<=N;i++)for(j=mx+m;j>=a[i].v;j--) 40 if(f[j]>f[j-a[i].v]+a[i].c)f[j]=f[j-a[i].v]+a[i].c; 41 ans=inf; 42 for(j=mx+m;j>=m;j--)if(f[j]+owner[j-m]<ans)ans=f[j]+owner[j-m]; 43 if(ans==inf)printf("-1\n");else printf("%d\n",ans); 44 return 0; 45 }
bzoj 2199: [Usaco2011 Jan]奶牛议会
2-SAT问题。。。这类问题似乎没法怎么包装。。。对于每头奶牛,如果它的某票没实现的话,另一票一定要实现。。。
因为有的议案结局未定。。而且数据范围不大。。直接暴力上。。。时间复杂度O(nm)
不知道如果缩点后搞的话怎么处理那些未定的议案TAT
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1023; 6 struct zs{ 7 int too,pre; 8 }e[8233]; 9 int last[maxn<<1],a,b; 10 bool must[maxn<<1]; 11 int i,j,k,n,m,tot; 12 char ans[maxn]; 13 int ra;char rx,x1,x2; 14 bool can0,can1; 15 inline int read(){ 16 rx=getchar();ra=0; 17 while(rx<'0'||rx>'9')rx=getchar(); 18 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 19 } 20 inline void insert(int a,int b){ 21 e[++tot].too=b;e[tot].pre=last[a];last[a]=tot; 22 } 23 void dfs(int x){ 24 must[x]=1;if(must[x>n?x-n:x+n])return; 25 for(int i=last[x];i;i=e[i].pre)if(!must[e[i].too])dfs(e[i].too); 26 } 27 inline bool judge(int x){ 28 memset(must,0,(n+n+1)); 29 dfs(x); 30 for(int i=n;i;i--)if(must[i]&&must[i+n])return 0; 31 return 1; 32 } 33 int main(){ 34 n=read();m=read(); 35 for(i=1;i<=m;i++){ 36 a=read();for(x1=getchar();x1!='Y'&&x1!='N';x1=getchar()); 37 if(x1=='N')a+=n; 38 b=read();for(x2=getchar();x2!='Y'&&x2!='N';x2=getchar()); 39 if(x2=='N')b+=n; 40 insert(a>n?a-n:a+n,b);insert(b>n?b-n:b+n,a); 41 } 42 for(i=1;i<=n;i++){ 43 can0=judge(i); 44 can1=judge(i+n); 45 if(can0&&can1)ans[i]='?'; 46 else if(can0)ans[i]='Y'; 47 else if(can1)ans[i]='N'; 48 else {puts("IMPOSSIBLE");return 0;} 49 } 50 for(i=1;i<=n;i++)putchar(ans[i]);printf("\n"); 51 return 0; 52 }
bzoj 1712: [Usaco2007 China]Summing Sums 加密
矩阵乘法。。。由题目得出,Ci'=sum-Ci,(sum表示前一轮中奶牛数字和)。本轮结束后sum'=sum*n-sum。
构造矩阵。。一个n行2列的矩阵A,第i行第一列为Ci,第二列为sum。
第二个矩阵B为2*2的矩阵,第一行为-1,0;第二行为1,(n-1)。
为啥卡了半天常数还是那么慢TAT
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 const ll modd=98765431; 7 const int maxn=50233; 8 ll pow[2][2],b[2][2],a[maxn][2]; 9 int i,j,k,n,m,T; 10 int ra;char rx; 11 inline int read(){ 12 rx=getchar();ra=0; 13 while(rx<'0'||rx>'9')rx=getchar(); 14 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 15 } 16 inline void mul1(ll a[2][2],ll b[2][2]){ 17 ll c[2][2];int i,j,k; 18 for(i=0;i<2;i++)for(j=0;j<2;j++) 19 for(c[i][j]=0,k=0;k<2;k++){c[i][j]+=a[i][k]*b[k][j];if(c[i][j]>=modd)c[i][j]%=modd;} 20 for(i=0;i<2;i++)for(j=0;j<2;j++)a[i][j]=c[i][j]; 21 } 22 inline void mul2(ll a[maxn][2],ll b[2][2]){ 23 ll c[maxn][2];i,j,k; 24 for(i=0;i<n;i++)for(j=0;j<2;j++) 25 for(c[i][j]=0,k=0;k<2;k++){c[i][j]+=a[i][k]*b[k][j];if(c[i][j]>=modd)c[i][j]%=modd;} 26 for(i=0;i<n;i++)for(j=0;j<2;j++)a[i][j]=c[i][j]; 27 } 28 int main(){ 29 n=read();T=read(); 30 for(i=0;i<n;i++)a[i][0]=read(),a[0][1]+=a[i][0],a[0][1]-=a[0][1]>=modd?modd:0; 31 for(i=1;i<n;i++)a[i][1]=a[0][1]; 32 pow[0][0]=pow[1][1]=1; 33 b[0][0]=-1;b[0][1]=0;b[1][0]=1;b[1][1]=n-1; 34 while(T){ 35 if(T&1) 36 mul1(pow,b); 37 T>>=1;if(T)mul1(b,b); 38 } 39 mul2(a,pow); 40 for(i=0;i<n;i++)printf("%lld\n",(a[i][0]+modd)%modd); 41 return 0; 42 }
bzoj 1583: [Usaco2009 Mar]Moon Mooing 哞哞叫
首先根据题目给出的式子,两种方式叫声时长都是递增的。。就变成了在两个递增的数列中取出前n小的。。
可以无脑的分别求出两种叫法的前n项再合并。。。。也可以只记录当前两种叫法分别叫到了多长时间,每次选下一次叫时间短的加入答案(因为可以当前项算出后一项时长)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int maxn=4002333; 7 int i,j,k,n,m,l1,l2,r; 8 ll c,a1,a2,c1,c2,b1,b2; 9 ll now,tmp1,tmp2,dl[maxn]; 10 int ra;char rx; 11 inline int read(){ 12 rx=getchar();ra=0; 13 while(rx<'0'||rx>'9')rx=getchar(); 14 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 15 } 16 int main(){ 17 c=read();n=read(); 18 a1=read();b1=read();c1=read(); 19 a2=read();b2=read();c2=read(); 20 l1=l2=1;r=1;dl[1]=c;tmp1=c*a1/c1+b1;tmp2=c*a2/c2+b2; 21 while(r<n){ 22 if(tmp1<tmp2){ 23 if(tmp1!=dl[r])dl[++r]=tmp1; 24 tmp1=dl[++l1]*a1/c1+b1; 25 }else{ 26 if(tmp2!=dl[r])dl[++r]=tmp2; 27 tmp2=dl[++l2]*a2/c2+b2; 28 } 29 } 30 printf("%lld\n",dl[r]); 31 return 0; 32 }
bzoj 1731: [Usaco2005 dec]Layout 排队布局
差分约束。。http://www.cnblogs.com/czllgzmzl/p/5072819.html
bzoj 2099: [Usaco2010 Dec]Letter 恐吓信
字符串。因为原信件里同一段内容可以多次截取。。所以每次剪出最长公共前缀就可以使总的剪裁次数最小。
求出后缀数组后,每次二分找lcp。。。时间复杂度(nlogn+mlogn*比较字符串大小的时间复杂度)(实际复杂度远低于最坏情况。。。比较字符串大小可以直接暴力)
注意一下二分的左边界(取决于具体写法= =)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 using namespace std; 7 const ll modd=223333333; 8 const int maxn=50233; 9 int i,j,k,n,m,l,r,mid,now,pos,ans,samelen,L,R,MID; 10 ll pre[maxn],pre1[maxn],jc[maxn],val1,val2; 11 int sa[maxn]; 12 char s[maxn],s1[maxn]; 13 int ra;char rx; 14 inline int read(){ 15 rx=getchar();ra=0; 16 while(rx<'0'||rx>'9')rx=getchar(); 17 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 18 } 19 inline int getlen(int s1,int s2){ 20 if(s[s1]!=s[s2])return 0; 21 l=1;r=n-(s1<s2?s2:s1)+1; 22 val1=pre[s1+r-1]-pre[s1-1]*jc[r]%modd;if(val1<0)val1+=modd; 23 val2=pre[s2+r-1]-pre[s2-1]*jc[r]%modd;if(val2<0)val2+=modd; 24 if(val1==val2)return r;r--; 25 while(l<r){ 26 mid=(l+r+1)>>1; 27 val1=pre[s1+mid-1]-pre[s1-1]*jc[mid]%modd;if(val1<0)val1+=modd; 28 val2=pre[s2+mid-1]-pre[s2-1]*jc[mid]%modd;if(val2<0)val2+=modd; 29 if(val1!=val2)r=mid-1;else l=mid; 30 } 31 return l; 32 } 33 bool cmp(int a,int b){ 34 now=getlen(a,b); 35 return s[a+now]<s[b+now]; 36 } 37 inline int get1(int l1,int l2){ 38 if(s[l1]!=s1[l2])return 0; 39 l=1;r=min(n-l1,m-l2)+1; 40 val1=pre[l1+r-1] -pre[l1-1] *jc[r]%modd;if(val1<0)val1+=modd; 41 val2=pre1[l2+r-1]-pre1[l2-1]*jc[r]%modd;if(val2<0)val2+=modd; 42 if(val1==val2)return r;r--; 43 while(l<r){ 44 mid=(l+r+1)>>1; 45 val1=pre[l1+mid-1] -pre[l1-1] *jc[mid]%modd;if(val1<0)val1+=modd; 46 val2=pre1[l2+mid-1]-pre1[l2-1]*jc[mid]%modd;if(val2<0)val2+=modd; 47 if(val1!=val2)r=mid-1;else l=mid; 48 // printf("%d %d-%d %lld %d-%d %lld\n",mid,l1,l1+mid-1,val1,l2,l2+mid-1,val2); 49 } 50 return l; 51 } 52 inline bool bigger(int l1,int l2){ 53 now=get1(l1,l2); 54 if(now>samelen)samelen=now; 55 // for(int i=l1;i<=n;i++)putchar(s[i]);putchar('|');for(int i=l2;i<=m;i++)putchar(s1[i]);printf(" %d\n",now); 56 return s[l1+now]>s1[l2+now]; 57 } 58 int main(){ 59 n=read();m=read();s[n+1]=0;s1[m+1]=1; 60 for(jc[0]=1,i=1;i<=n;pre[i]=(pre[i-1]*(ll)197+(ll)s[i])%modd,jc[i]=jc[i-1]*197%modd,i++)for(s[i]=getchar();s[i]<'A'||s[i]>'Z';s[i]=getchar()); 61 for(i=1;i<=m;pre1[i]=(pre1[i-1]*(ll)197+(ll)s1[i])%modd,i++)for(s1[i]=getchar();s1[i]<'A'||s1[i]>'Z';s1[i]=getchar()); 62 // for(i=1;i<=n;i++)printf("%lld ",pre[i]);printf("\n"); 63 // for(i=1;i<=m;i++)printf("%lld ",pre1[i]);printf("\n"); 64 65 for(i=1;i<=n;i++)sa[i]=i; 66 sort(sa+1,sa+1+n,cmp); 67 // for(i=1;i<=n;i++)printf("%d\n",sa[i]); 68 // for(i=1;i<=n;i++)printf(" %d\n",sa[i]); 69 for(pos=1;pos<=m;now=samelen=0){ 70 L=1;R=n; 71 while(L<=R){ 72 MID=(L+R)>>1; 73 if(bigger(sa[MID],pos))R=MID-1;else L=MID+1; 74 } 75 // for(i=sa[L];i<=n;i++)putchar(s[i]);printf("\n"); 76 if(samelen<m-pos+1&&L<n)samelen=max(samelen,get1(sa[L+1],pos)); 77 ans++;pos+=samelen;//printf(" %d\n",pos); 78 } 79 printf("%d\n",ans); 80 return 0; 81 }
bzoj 1577: [Usaco2009 Feb]庙会捷运Fair Shuttle
把奶牛按目的地升序排序,然后直接依次尽量满足。。正确性同bzoj1828.。。。
具体用线段树实现(区间加&&查询区间最小值)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=20233; 8 struct zs{ 9 int t,s,num; 10 }a[50233]; 11 int l[maxn<<1],r[maxn<<1],mn[maxn<<1],mid[maxn<<1],del[maxn<<1]; 12 int i,j,k,n,m,c,num,ans,tot; 13 int ra;char rx; 14 inline int read(){ 15 rx=getchar();ra=0; 16 while(rx<'0'||rx>'9')rx=getchar(); 17 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 18 } 19 void build(int a,int b){ 20 int now=++tot;mn[now]=c; 21 mid[now]=(a+b)>>1; 22 if(a<b){ 23 l[now]=tot+1;build(a,mid[now]); 24 r[now]=tot+1;build(mid[now]+1,b); 25 } 26 } 27 inline void pushdown(int now,int l,int r){ 28 del[l]+=del[now];del[r]+=del[now]; 29 mn[l]-=del[now];mn[r]-=del[now];del[now]=0; 30 } 31 void insert(int now,int a,int b,int c,int d,int num){ 32 if(c<=a&&d>=b){del[now]+=num,mn[now]-=num;return;} 33 if(del[now])pushdown(now,l[now],r[now]); 34 if(c<=mid[now])insert(l[now],a,mid[now],c,d,num); 35 if(d>mid[now])insert(r[now],mid[now]+1,b,c,d,num); 36 mn[now]=mn[l[now]]<mn[r[now]]?mn[l[now]]:mn[r[now]]; 37 } 38 int query(int now,int a,int b,int c,int d){ 39 if(c<=a&&d>=b)return mn[now]; 40 if(del[now])pushdown(now,l[now],r[now]); 41 int tmp=233333333; 42 if(c<=mid[now])tmp=query(l[now],a,mid[now],c,d); 43 if(d>mid[now])tmp=min(tmp,query(r[now],mid[now]+1,b,c,d)); 44 return tmp; 45 } 46 bool cmp(zs a,zs b){return a.t<b.t;} 47 int main(){ 48 m=read();n=read();c=read(); 49 for(i=1;i<=m;i++)a[i].s=read(),a[i].t=read()-1,a[i].num=read(); 50 build(1,n); 51 sort(a+1,a+1+m,cmp); 52 for(i=1;i<=m;i++)num=min(a[i].num,query(1,1,n,a[i].s,a[i].t)),insert(1,1,n,a[i].s,a[i].t,num),ans+=num; 53 printf("%d\n",ans); 54 return 0; 55 }
bzoj 2274: [Usaco2011 Feb]Generic Cow Protests
dp优化。。f[i]表示划分前i个数的方案总数。。f[0]=1;f[i]=sum{ f[j] },(j<i且presum[i]>=presum[j])(presum表示前缀和)
所以把presum排序后,按原顺序算。。就是求排序后的前缀和。树状数组还是线段树什么的随意啦。。结果写的sgt比kpm的树状数组慢了三分之一= =
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=132333; 7 const int modd=1000000009; 8 int sum[maxn<<1],rk[100233],pos[100233],pre[100233],a[100233],f[100233]; 9 int i,j,k,n,m,x,size,tot; 10 int ra,fh;char rx; 11 inline int read(){ 12 rx=getchar();ra=0;fh=1; 13 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 14 if(rx=='-')fh=-1,rx=getchar(); 15 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 16 } 17 bool cmp(int aa,int b){return pre[aa]<pre[b];} 18 int main(){ 19 n=read(); 20 for(i=1;i<=n;rk[i]=i,pre[i]=pre[i-1]+a[i],i++)a[i]=read(); 21 sort(rk+1,rk+1+n,cmp);for(i=1;i<=n;i++)tot+=(pre[rk[i]]!=pre[rk[i-1]])||i==1,pos[rk[i]]=tot; 22 size=1;while(size<tot)size<<=1;size--; 23 for(i=1;i<=n;i++){ 24 for(x=pos[i]+size,f[i]=sum[x]+(pre[i]>=0);(x&(-x))!=x;x>>=1)if(x&1){f[i]+=sum[x^1];if(f[i]>=modd)f[i]-=modd;} 25 for(x=pos[i]+size;x;x>>=1){sum[x]+=f[i];if(sum[x]>=modd)sum[x]-=modd;} 26 } 27 printf("%d\n",f[n]); 28 return 0; 29 }
bzoj 1594: [Usaco2008 Jan]猜数游戏
二分答案+并查集判定 http://www.cnblogs.com/czllgzmzl/p/5074066.html
bzoj 1744: [Usaco2005 oct]Skiing 奶牛滑雪
最短路。。看了kpm代码才发现倒着跑会方便一些。。速度的变化就好弄了。。就是把后面的路径(耗时)长度都乘上速度的变化。。。
假设初始速度为1,最后把时间除以真正的初始速度就好了
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=20233; 8 int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0}; 9 int dlx[502333],dly[502333]; 10 int map[103][103]; 11 double tmpdis,v0,dis[103][103],nowdis; 12 double two[51]; 13 bool u[103][103]; 14 int i,j,k,n,m,c,ans,l,r,x,y,nowx,nowy; 15 int ra,fh;char rx; 16 inline int read(){ 17 rx=getchar();ra=0;fh=1; 18 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 19 if(rx=='-')fh=-1,rx=getchar(); 20 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 21 } 22 inline double get(double x,int delta){ 23 if(delta>=0)return x*two[delta]; 24 return x/two[-delta]; 25 } 26 int main(){ 27 for(two[0]=1.0,i=1;i<=50;i++)two[i]=two[i-1]*2.0; 28 double inf=1<<30;inf*=inf; 29 v0=read();n=read();m=read(); 30 for(i=1;i<=n;i++)for(j=1;j<=m;j++)map[i][j]=read(),dis[i][j]=inf; 31 dis[n][m]=0.0; 32 dlx[1]=n;dly[1]=m;l=0;r=1;u[n][m]=1; 33 while(l<r){ 34 nowx=dlx[++l];nowy=dly[l];nowdis=dis[nowx][nowy]; 35 for(i=0;i<4;i++){ 36 x=nowx+xx[i];y=nowy+yy[i]; 37 if(x<1||x>n||y<1||y>m)continue; 38 tmpdis=get(nowdis,map[nowx][nowy]-map[x][y])+1.0; 39 if(tmpdis<dis[x][y]) 40 if(!u[x][y])dis[x][y]=tmpdis,dlx[++r]=x,dly[r]=y,u[x][y]=1; 41 else dis[x][y]=tmpdis; 42 } 43 u[nowx][nowy]=0; 44 } 45 printf("%.2lf\n",dis[1][1]/v0); 46 return 0; 47 }
bzoj 1663: [Usaco2006 Open]赶集
最长路。。如果接完一个点的礼物后还来得及去另外的一个点接礼物,就在这两点间连条单向边。
因为出发时不一定要等点1的礼物,所以新建一个点0,与出发时能赶去接礼物的点连边 。最后答案就是最长路长度了
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=402; 8 struct zs{ 9 int too,pre; 10 }e[160433]; 11 int last[maxn],dis[maxn],dl[23333],tim[maxn]; 12 int i,j,k,n,m,ans,l,r,tot,b,s,now,to; 13 bool u[maxn]; 14 int ra;char rx; 15 inline int read(){ 16 rx=getchar();ra=0; 17 while(rx<'0'||rx>'9')rx=getchar(); 18 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 19 } 20 inline void insert(int a,int b){ 21 e[++tot].too=b;e[tot].pre=last[a];last[a]=tot; 22 } 23 int main(){ 24 n=read(); 25 for(i=1;i<=n;i++)tim[i]=read(); 26 for(i=1;i<=n;i++)for(j=1;j<=n;j++){ 27 b=read(); 28 if(i!=j&&tim[j]-tim[i]>=b)insert(i,j); 29 if(i==1&&tim[j]>=b)insert(0,j); 30 } 31 s=0;u[s]=1;l=0;r=1;dl[1]=s;dis[s]=0; 32 while(l<r){ 33 now=dl[++l]; 34 for(i=last[now],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(dis[to]<dis[now]+1) 35 if(!u[to])u[to]=1,dl[++r]=to,dis[to]=dis[now]+1; 36 else dis[to]=dis[now]+1; 37 u[now]=0; 38 } 39 for(i=1;i<=n;i++)if(dis[i]>ans)ans=dis[i]; 40 printf("%d\n",ans); 41 return 0; 42 }
bzoj 1735: [Usaco2005 jan]Muddy Fields 泥泞的牧场
二分图最大匹配。类似1741小行星那题。。不同的是多了个约束条件,木板只能在泥地里放。
先按行处理出各泥地的编号(同一行且联通的泥地算同个点),列的时候同理
对于每个泥泞的点,把它所在的行编号和所在的列编号这两点间连边。。最后求二分图最大匹配
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=26*50*2+2; 6 struct zs{ 7 int too,pre; 8 bool flow; 9 }e[202333]; 10 int last[maxn],dis[maxn],s,t,tot=1; 11 int dl[maxn]; 12 int bell[51][51],belh[51][51],lnum,hnum; 13 int i,j,k,n,m,ans; 14 char map[51][51]; 15 bool u[maxn]; 16 int ra;char rx; 17 inline int read(){ 18 rx=getchar();ra=0; 19 while(rx<'0'||rx>'9')rx=getchar(); 20 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 21 } 22 inline bool bfs(){ 23 int l=0,r=1,now,i; 24 memset(dis,255,(t+1)<<2);dis[s]=0;dl[1]=s; 25 while(l<r){ 26 now=dl[++l]; 27 for(i=last[now];i;i=e[i].pre)if(dis[e[i].too]==-1&&e[i].flow) 28 dl[++r]=e[i].too,dis[e[i].too]=dis[now]+1; 29 } 30 return dis[t]!=-1; 31 } 32 int dfs(int x,int mx){ 33 if(x==t)return mx; 34 int used=0,i,w,to; 35 for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(e[i].flow&&dis[to]==dis[x]+1){ 36 w=dfs(e[i].too,1);if(w){ 37 e[i].flow=0;e[i^1].flow=1; 38 used++; 39 if(used==mx)return mx; 40 } 41 } 42 dis[x]=-1;return used; 43 } 44 inline void insert(int a,int b){ 45 e[++tot].too=b;e[tot].flow=1;e[tot].pre=last[a];last[a]=tot; 46 e[++tot].too=a;e[tot].flow=0;e[tot].pre=last[b];last[b]=tot; 47 } 48 int main(){ 49 n=read();m=read(); 50 for(i=1;i<=n;i++)for(j=1;j<=m;j++)for(map[i][j]=getchar();map[i][j]!='*'&&map[i][j]!='.';map[i][j]=getchar()); 51 for(i=1;i<=n;i++) 52 for(j=1;j<=m;j++)if(map[i][j]=='*'){ 53 if(map[i][j-1]!='*')hnum++; 54 belh[i][j]=hnum; 55 }lnum=hnum; 56 for(j=1;j<=m;j++)for(i=1;i<=n;i++)if(map[i][j]=='*'){ 57 if(map[i-1][j]!='*')lnum++; 58 bell[i][j]=lnum; 59 } 60 for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(map[i][j]=='*')insert(belh[i][j],bell[i][j]); 61 s=0;t=lnum+1; 62 for(i=1;i<=hnum;i++)insert(s,i);for(i=hnum+1;i<=lnum;i++)insert(i,t); 63 while(bfs())ans+=dfs(s,233333333); 64 printf("%d\n",ans); 65 return 0; 66 }
bzoj 2196: [Usaco2011 Mar]Brownie Slicing
二分答案+判定。。。。
JSZX11556:我随手写的怎么就#1了。。。。不过后来kpm压常抢了#1.。%%%
二分答案设为mid。。判定就是看是否能分出A段连续的行,每一段里面竖着分成B份,使得每一份都>=mid。划分的时候都贪心地试就行了。。。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=503; 6 int map[maxn][maxn],a[maxn]; 7 int i,j,k,n,m,sum,A,B,l,r,mid; 8 int ra;char rx; 9 inline int read(){ 10 rx=getchar();ra=0; 11 while(rx<'0'||rx>'9')rx=getchar(); 12 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 13 } 14 inline bool can(int h,int x){ 15 register int i;int sum=0,num=0; 16 for(i=1;i<=m;i++)a[i]+=map[h][i]; 17 for(i=1;i<=m&&sum<B;i++){ 18 num+=a[i]; 19 if(num>=x)num=0,sum++; 20 } 21 return sum>=B; 22 } 23 inline bool judge(int x){ 24 sum=0; 25 memset(a,0,(m+1)<<2); 26 for(i=1;i<=n&&sum<A;i++)if(can(i,x))sum++,memset(a,0,(m+1)<<2); 27 return sum>=A; 28 } 29 int main(){ 30 n=read();m=read();A=read();B=read(); 31 for(i=1;i<=n;i++)for(j=1;j<=m;j++)map[i][j]=read(),r+=map[i][j]; 32 l=1;r/=(A*B); 33 while(l<r){ 34 mid=(l+r+1)>>1; 35 if(judge(mid))l=mid;else r=mid-1; 36 } 37 printf("%d\n",l); 38 return 0; 39 }
bzoj 2059: [Usaco2010 Nov]Buying Feed 购买饲料
数据范围是n<=500,k<=10000。。dp+单调队列优化
f[i][j]表示到第i个商店后,车上有j吨饲料的最小花费。
f[i][j]=min{ f[i-1][k]+dis(i-1,i)*k^2+cost[i]*(j-k) },(k<=j,dis表示两点间距离,cost[i]表示商店i的饲料价格)
也就是f[i][j]=cost[i]*j + min{ f[i-1][k]+dis(i-1,i)*k^2-cost[i]*k }。后面那个就可以用单调队列优化了。。
偷懒调了优先队列。。多了个log果然慢成狗= =。。一开始脑残wa了
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #define ll long long 7 using namespace std; 8 const int maxn=100005; 9 const int maxk=200023; 10 ll f[maxk]; 11 struct zs{ 12 int k;ll val; 13 }; 14 struct poi{ 15 int pos,rest,cost; 16 }a[maxn]; 17 int i,j; 18 ll n,m,K,ed; 19 20 int ra,fh;char rx; 21 inline int read(){ 22 rx=getchar();ra=0;fh=1; 23 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 24 if(rx=='-')rx=getchar(),fh=-1; 25 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 26 } 27 28 bool cmp(poi a,poi b){return a.pos<b.pos;} 29 bool operator <(zs a,zs b){ 30 return a.val>b.val; 31 } 32 int main(){ 33 K=read();ed=read();n=read(); 34 for(i=1;i<=n;i++)a[i].pos=read(),a[i].rest=read(),a[i].cost=read(); 35 sort(a+1,a+1+n,cmp); 36 memset(f,50,(K+1)<<3);f[0]=0; 37 for(i=1;i<=n;i++){ 38 priority_queue<zs>q; 39 while(!q.empty())q.pop(); 40 ll dis=a[i].pos-a[i-1].pos; 41 q.push((zs){0,0}); 42 for(j=1;j<=K;j++){ 43 q.push((zs){j,f[j]-(ll)j*a[i].cost+dis*j*j}); 44 while(!q.empty()&&q.top().k<j-a[i].rest)q.pop(); 45 f[j]=q.top().val+(ll)j*a[i].cost; 46 } 47 } 48 printf("%lld\n",f[K]+(ll)(ed-a[n].pos)*K*K); 49 return 0; 50 }
bzoj 1777: [Usaco2010 Hol]rocks 石头木头
博弈论。。。求当前局面的sg值
如果不考虑每次取的数量的限制的话这题就是个阶梯nim(以深度为阶梯)。。。只考虑把石头从奇数深度挪到偶数深度。(根节点深度为0)
但是有L的限制。。对每个奇数深度的节点就变成俩人轮流取,一次最多取L个。。。sg值就是石头数量对(L+1)取模后的值。。。偶数深度的节点sg值为0.
把各个节点的sg值异或起来就是整个局面的sg值了(节点间互不影响)。修改操作的时候,就把总的sg值和 修改的节点原来的sg值 异或一下,在异或下新的sg值。
总的sg值为0的话先手必败。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=10233; 6 struct zs{ 7 int too,pre; 8 }e[23333]; 9 int last[maxn],dep[maxn],tot; 10 int num[maxn]; 11 int i,j,k,n,m,t,r,ans,a,b; 12 inline void insert(int a,int b){ 13 e[++tot].too=b;e[tot].pre=last[a];last[a]=tot; 14 } 15 void dfs(int x,int depth){ 16 dep[x]=depth; 17 for(int i=last[x];i;i=e[i].pre)dfs(e[i].too,depth^1); 18 } 19 int ra,fh;char rx; 20 inline int read(){ 21 rx=getchar();ra=0;fh=1; 22 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 23 if(rx=='-')rx=getchar(),fh=-1; 24 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 25 } 26 inline int getsg(int x){ 27 if(dep[x])return num[x]%(r+1); 28 else return 0; 29 } 30 int main(){ 31 n=read();t=read();r=read(); 32 for(i=2;i<=n;i++) 33 a=read(),num[i]=read(),insert(a,i); 34 dfs(1,0); 35 t--; 36 a=read();b=read();num[a]=b; 37 for(i=2;i<=n;i++)ans^=getsg(i); 38 printf("%s\n",ans?"Yes":"No"); 39 while(t--){ 40 a=read();b=read(); 41 ans^=getsg(a); 42 num[a]=b; 43 ans^=getsg(a); 44 printf("%s\n",ans?"Yes":"No"); 45 } 46 return 0; 47 }
bzoj 1733: [Usaco2005 feb]Secret Milking Machine 神秘的挤奶机
二分答案为mid,s连1,n连t,把长度不超过mid的边加进新图中,容量为1,看新图中最大流是否>=T。
二分前可以先按边的长度离散化一下。。。反正最后的答案一定等于某条边的长度
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=201; 7 struct zs{ 8 int too,pre; 9 bool flow; 10 }e[160033]; 11 int last[maxn],dis[maxn],cur[maxn],x[40023],y[40023],dist[40023],tmp[40023]; 12 int dl[maxn]; 13 int i,j,k,n,m,s,t,a,b,c,tot,T,mx,l,r,mid,ans; 14 int ra;char rx; 15 inline int read(){ 16 rx=getchar();ra=0; 17 while(rx<'0'||rx>'9')rx=getchar(); 18 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 19 } 20 inline void insert(int a,int b){ 21 e[++tot].too=b;e[tot].flow=1;e[tot].pre=last[a];last[a]=tot; 22 e[++tot].too=a;e[tot].flow=0;e[tot].pre=last[b];last[b]=tot; 23 } 24 inline bool bfs(){ 25 memset(dis,255,(t+1)<<2); 26 int to,l=0,r=1,i,now;dl[1]=s;dis[s]=0; 27 while(l<r){ 28 now=dl[++l]; 29 for(i=last[now],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(e[i].flow&&dis[to]==-1) 30 dis[to]=dis[now]+1,dl[++r]=to; 31 } 32 return dis[t]!=-1; 33 } 34 int dfs(int x,int mx){ 35 if(x==t)return mx; 36 int used=0,w;register int i,to; 37 for(i=cur[x],to=e[i].too;i;cur[x]=i,i=e[i].pre,to=e[i].too)if(e[i].flow&&dis[to]==dis[x]+1){ 38 w=dfs(to,1);if(w){ 39 e[i].flow=0;e[i^1].flow=1; 40 used++;if(used==mx)return mx; 41 } 42 } 43 dis[x]=-1;return used; 44 } 45 int main(){ 46 n=read();m=read();T=read();s=1;t=n; 47 for(i=1;i<=m;i++)x[i]=read(),y[i]=read(),dist[i]=tmp[i]=read(); 48 sort(tmp+1,tmp+1+m); 49 l=0;r=m; 50 while(l<r){ 51 mid=(l+r)>>1; 52 tot=1;memset(last,0,(t+1)<<2); 53 for(i=1;i<=m;i++)if(dist[i]<=tmp[mid])insert(x[i],y[i]),insert(y[i],x[i]); 54 ans=0; 55 while(bfs()&&ans<T)memcpy(cur,last,(t+1)<<2),ans+=dfs(s,2002333); 56 if(ans>=T)r=mid;else l=mid+1; 57 } 58 printf("%d\n",tmp[l]); 59 return 0; 60 }
bzoj 1740: [Usaco2005 mar]Yogurt factory 奶酪工厂
傻逼题。。没错我是连傻逼题都不会写的傻逼。
记录一下到当前,单位奶酪的最小代价就好了。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 int S,n; 7 int ra;char rx; 8 inline ll read(){ 9 rx=getchar();ra=0; 10 while(rx<'0'||rx>'9')rx=getchar(); 11 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 12 } 13 int main(){ 14 n=read();S=read(); 15 register int now,i,low=100233333;register ll ans=0; 16 for(i=n;i;low+=S,i--){ 17 now=read();if(now<low)low=now; 18 ans+=read()*low; 19 } 20 printf("%lld\n",ans); 21 return 0; 22 }
bzoj 1742: [Usaco2005 nov]Grazing on the Run 边跑边吃草
区间dp(因为走到哪里吃到哪里。。吃了的范围肯定是一段区间)。中英文数据范围不一致= =其实数据范围只有n<=1000.。。
f[i][j][0]表示吃完[i,j]这段区间里的草后站在左端点(i点)的最少腐败值。f[i][1]表示吃完[i,j]后站在右端点的最少腐败值。dis(i,j)表示点i与点j间的距离
初始化f[i][i][0]=f[i][i][1]=dis(i,p)*n。
f[i][j][0]=min{ f[i+1][j][0]+dis(i,i+1)*( n-(j-i) ),f[i+1][j][1]+dis(i,j)*( n-(j-i) ) }...f[i][j][1]类似,就是从f[i][j-1][0..1]转移过来。。
行走距离乘上(n-(j-i))是因为走这段路时,会使还没吃的(n-(j-i))棵草的腐败值都加上所花的时间。
如果用f[i][j][0..1]表示吃了的区间长度为i,区间以j结尾,吃完站在左右端点的最小腐败值,就可以省掉一维= =
压了下常数然后#1了。。。。然而代码更丑了TAT。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=3001; 8 int f[maxn][2]; 9 int len,s,mxj,n; 10 int a[maxn],b[maxn]; 11 int ra;char rx; 12 inline int read(){ 13 rx=getchar();ra=0; 14 while(rx<'0'||rx>'9')rx=getchar(); 15 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 16 } 17 int main(){ 18 int i,j,mxj,tmp; 19 n=read();s=read(); 20 for(i=1;i<=n;i++)a[i]=read();sort(a+1,a+1+n); 21 for(i=1;i<=n;b[i]=a[i+1]-a[i],i++)f[i][0]=f[i][1]=abs(s-a[i])*n; 22 for(i=1,mxj=n-1;i<n;mxj--,i++)for(j=1;j<=mxj;j++) 23 tmp=f[j][0],f[j][0]=min(f[j+1][0]+mxj*b[j],f[j+1][1]+mxj*(a[j+i]-a[j])), 24 f[j][1]=min(tmp+mxj*(a[j+i]-a[j]),f[j][1]+mxj*b[j+i-1]); 25 printf("%d\n",min(f[1][0],f[1][1])); 26 return 0; 27 }
bzoj 3126: [Usaco2013 Open]Photo
dp....先把区间按右端点排序。。设题目给出的区间为l[],r[]
f[i]表示点1~第i个区间的右端点 中可能的点的最大数目。。
f[i]=max{ f[j]+1 },(不存在任意一段区间满足j<L[k],R[k]<i,也不存在任意一段区间满足L[k]<=j,R[k]>=i)。。。。
所以可以预处理一下。。。不清楚如何用单调队列写TAT。。只好写个线段树了。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 const int maxn=200233; 8 struct zs{ 9 int l,r; 10 }a[maxn],b[maxn]; 11 int minl[maxn],maxl[maxn],mp[maxn],f[maxn]; 12 int mx[maxn<<2]; 13 int i,j,n,m,tmpl,top,tmp,ans,size; 14 bool can[maxn]; 15 struct poi{ 16 int pos,val; 17 }; 18 priority_queue<poi>q; 19 bool operator <(poi a,poi b){return a.val<b.val;} 20 bool cmp1(zs a,zs b){return a.r<b.r;} 21 bool cmp2(zs a,zs b){return a.r>b.r;} 22 int ra;char rx; 23 inline int read(){ 24 rx=getchar();ra=0; 25 while(rx<'0'||rx>'9')rx=getchar(); 26 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 27 } 28 inline void update(int x,int val){ 29 for(x+=size;x&&mx[x]<val;x>>=1)mx[x]=val; 30 } 31 inline int getmax(int l,int r){ 32 // printf(" %d--%d:",l,r); 33 l+=size;r+=size;int ans=max(mx[l],mx[r]); 34 if(l<=size){ 35 if(r>=size)ans=max(ans,0); 36 l=size+1; 37 } 38 if(l>=r)return ans; 39 ans=max(ans,mx[l]); 40 // printf(" %d--%d:",l-size,r-size); 41 while((l^1)!=r){ 42 if((!(l&1))&&mx[l^1]>ans)ans=mx[l^1]; 43 if((r&1)&&mx[r^1]>ans)ans=mx[r^1]; 44 l>>=1;r>>=1; 45 } 46 // printf("%d\n",ans); 47 return ans; 48 } 49 int main(){ 50 m=read();n=read(); 51 for(i=1;i<=n;i++)a[i].l=read(),a[i].r=read(),mp[a[i].l]++,mp[a[i].r+1]--; 52 for(i=1,tmp=0;i<=m;i++)tmp+=mp[i],can[i]=(tmp>0); 53 memcpy(b,a,(n+1)<<3); 54 sort(b+1,b+1+n,cmp1);top=n;tmpl=m+1; 55 for(i=m;i;i--){ 56 while(top&&b[top].r>=i)tmpl=min(tmpl,b[top--].l); 57 minl[i]=min(i,tmpl); 58 if(!can[i])minl[i]=m+1; 59 }tmpl=-1; 60 for(i=top=1;i<=m;i++){ 61 while(top<=n&&b[top].r<i)tmpl=max(tmpl,b[top++].l); 62 maxl[i]=tmpl-1; 63 if(!can[i])maxl[i]=-1; 64 } 65 // for(i=1;i<=m;i++)printf(" %d %d\n",maxl[i]+1,minl[i]-1); 66 memset(f,180,(m+1)<<2);f[0]=0; 67 for(size=1;size<m;size<<=1);size--; 68 memset(mx,180,(size*2+2)<<2); 69 for(i=1;i<=m;i++){ 70 if(maxl[i]+1<minl[i])f[i]=getmax(maxl[i]+1,minl[i]-1)+1,update(i,f[i]);//,printf(" %d %d\n",i,f[i]); 71 ans=max(ans,f[i]); 72 // printf(" %d %d\n",i,f[i]); 73 } 74 for(i=1;i<=n;i++)if(getmax(b[i].l,b[i].r)<=0){puts("-1");return 0;} 75 printf("%d\n",ans); 76 return 0; 77 }
bzoj 1722: [Usaco2006 Mar] Milk Team Select 产奶比赛
树形dp。。f[i][j][0]表示在以i为根的子树中(不取i),有j对血缘关系,产出牛奶的最大值。f[i][j][1]同理,但要取i。。
求f[i][j][0]的时候,就是对各个孩子跑一遍01背包= =
f[i][j][1]类似,但是注意血缘关系= =。。。。太蛋疼具体就不写了TAT
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=505; 6 struct zs{ 7 int too,pre; 8 }e[23333]; 9 int last[maxn],tot; 10 int f[maxn][maxn][3];//f[i][j][2]=max(f[i][j][0],f[i][j][1])... 11 int pre[maxn],size[maxn]; 12 int val[maxn]; 13 int i,j,k,n,m,x,a,sum; 14 int ra,fh;char rx; 15 inline int read(){ 16 rx=getchar();ra=0;fh=1; 17 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 18 if(rx=='-')rx=getchar(),fh=-1; 19 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 20 } 21 inline void insert(int a,int b){ 22 e[++tot].too=b;e[tot].pre=last[a];last[a]=tot; 23 } 24 void dfs(int x){ 25 int i,j,to,k,mx,k1;size[x]=1; 26 memset(f[x],233,sizeof(f[x])); 27 f[x][0][0]=0;f[x][0][1]=val[x];f[x][0][2]=max(0,val[x]); 28 for(i=last[x];i;i=e[i].pre)dfs(e[i].too),size[x]+=size[e[i].too]; 29 if(size[x]==1)return; 30 if(x!=0) 31 for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too){ 32 mx=size[to];while(mx&&f[to][mx][2]<=-100233333)mx--; 33 for(j=0;j<size[x];j++)pre[j]=f[x][j][1]; 34 for(j=size[x]-1;j>=0;j--){ 35 for(k=0;k<=mx;k++)f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+f[to][k][2]); 36 if(pre[0]+f[to][j][0]>f[x][j][1])f[x][j][1]=pre[0]+f[to][j][0]; 37 38 if(mx>=j)mx=j-1; 39 for(k=0,k1=j-k;k<=mx&&k<j;k++,k1--){ 40 if(pre[k1]+f[to][k][0]>f[x][j][1])f[x][j][1]=pre[k1]+f[to][k][0]; 41 if(pre[k1-1]+f[to][k][1]>f[x][j][1])f[x][j][1]=pre[k1-1]+f[to][k][1]; 42 } 43 } 44 }else 45 for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too) 46 for(j=n-1;j>=0;j--)for(k=0;k<=j;k++)f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+f[to][k][2]); 47 for(i=0;i<n;i++)f[x][i][2]=f[x][i][0]>f[x][i][1]?f[x][i][0]:f[x][i][1]; 48 } 49 int main(){ 50 n=read();x=read(); 51 for(i=1;i<=n;i++){ 52 val[i]=read(),a=read(),insert(a,i); 53 if(val[i]>=0)sum+=val[i]; 54 } 55 if(sum<x){puts("-1");return 0;} 56 dfs(0); 57 for(i=n-1;f[0][i][0]<x;i--); 58 printf("%d\n",i); 59 return 0; 60 }
bzoj 1779: [Usaco2010 Hol]Cowwar 奶牛战争
比较简单的最大流。。。四分图233。。每个点拆成四个点。。假设图画起来是四列,每列n个点的。。
1、第一列中John的奶牛与S相连;
2、第一列John的每头奶牛与第二列中的可达位置(原地或相邻,且没有Tom的牛)相连;
3、第二列中的每个可达位置与第三列中的自己连一条边。(一个点上只能站一只牛)
4、第三列每个位置与它第四列中相邻的(能攻击到的)、有Tom的牛的位置相连。最后第四列的Tom的牛连一条到T的边。。。
各边容量都为1。。。求出来的最大流就是答案。。。可以把图中一些没用的点去掉= =
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=4023; 6 struct zs{ 7 int too,pre; 8 bool flow; 9 }e[32333]; 10 struct edge{ 11 int too,pre; 12 }E[10233]; 13 int last[maxn],tot=1,LAST[1002],TOT,ans; 14 int jim[1002],tom[1002],id[1002],cow[1002]; 15 short dis[maxn]; 16 int dl[maxn]; 17 18 int s,t,i,j,a,b,n,m,to; 19 bool istom[1002]; 20 int ra;char rx; 21 inline int read(){ 22 rx=getchar();ra=0; 23 while(rx<'0'||rx>'9')rx=getchar(); 24 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 25 } 26 inline void insert(int a,int b){ 27 // printf(" %d-->%d\n",a,b); 28 e[++tot].too=b;e[tot].flow=1;e[tot].pre=last[a];last[a]=tot; 29 e[++tot].too=a;e[tot].flow=0;e[tot].pre=last[b];last[b]=tot; 30 } 31 inline void insmap(int a,int b){ 32 E[++TOT].too=b;E[TOT].pre=LAST[a];LAST[a]=TOT; 33 E[++TOT].too=a;E[TOT].pre=LAST[b];LAST[b]=TOT; 34 } 35 inline bool bfs(){ 36 int l=0,r=1,i,now; 37 memset(dis,255,(t+1)<<1); 38 dl[1]=s;dis[s]=0; 39 while(l<r) 40 for(now=dl[++l],i=last[now];i;i=e[i].pre) 41 if(e[i].flow&&dis[e[i].too]==-1) 42 dl[++r]=e[i].too,dis[e[i].too]=dis[now]+1; 43 return dis[t]!=-1; 44 } 45 int dfs(int x,int mx){ 46 if(x==t)return mx; 47 int used=0,i,to,w;dis[x]++; 48 for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(e[i].flow&&dis[to]==dis[x]){ 49 w=dfs(to,1);if(w){ 50 e[i].flow=0;e[i^1].flow=1; 51 used++;if(used==mx){dis[x]--;return mx;} 52 } 53 } 54 dis[x]=-1;return used; 55 } 56 int main(){ 57 n=read();m=read();s=0;int numj=0,numt=0; 58 for(i=1;i<=n;i++){ 59 for(rx=getchar();rx!='T'&&rx!='E'&&rx!='J';rx=getchar()); 60 if(rx=='J')insert(s,++numj),jim[i]=numj,cow[numj]=i; 61 else if(rx=='T')tom[i]=++numt,istom[i]=1; 62 }int tmp=0; 63 for(i=1;i<=n;i++)if(!istom[i])id[i]=++tmp; 64 t=numj+(n-numt)*2+numt+1; 65 for(i=1;i<=m;i++)a=read(),b=read(),insmap(a,b); 66 for(i=last[0];i;i=e[i].pre){ 67 to=cow[e[i].too];insert(jim[to],numj+id[to]); 68 for(j=LAST[to];j;j=E[j].pre)if(!istom[E[j].too]) 69 insert(jim[to],numj+id[E[j].too]); 70 } 71 for(i=1;i<=n;i++)if(!istom[i]){ 72 insert(numj+id[i],numj+id[i]+(n-numt)); 73 for(j=LAST[i];j;j=E[j].pre)if(istom[E[j].too]) 74 insert(numj+id[i]+(n-numt),numj+(n-numt)*2+tom[E[j].too]); 75 }else insert(numj+(n-numt)*2+tom[i],t); 76 while(bfs())ans+=dfs(s,100002333); 77 printf("%d\n",ans); 78 return 0; 79 }
bzoj 1739: [Usaco2005 mar]Space Elevator 太空电梯
背包dp。。因为最大高度才4w。。。所以直接用f[i]表示能否堆到i这个高度。
把方块按最大高度升序排序,按顺序对每种方块i跑多重背包。。。似乎也可以sxbk把f数组换成bitset。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=502; 7 struct zs{ 8 int mx,c,h; 9 }a[maxn]; 10 bool f[40233]; 11 int i,j,n,m,ans,k,nowc,nowh,nowmx; 12 int ra,fh;char rx; 13 inline int read(){ 14 rx=getchar();ra=0;fh=1; 15 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 16 if(rx=='-')rx=getchar(),fh=-1; 17 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 18 } 19 bool cmp(zs a,zs b){return a.mx<b.mx;} 20 int main(){ 21 n=read();f[0]=1; 22 for(i=1;i<=n;i++)a[i].h=read(),a[i].mx=read(),a[i].c=read(),ans=max(ans,a[i].mx); 23 sort(a+1,a+1+n,cmp); 24 for(i=1;i<=n;i++){nowc=a[i].c;nowh=a[i].h;nowmx=a[i].mx; 25 for(j=1;j<=nowc;j++) 26 for(k=nowmx;k>=nowh;k--)f[k]|=f[k-nowh]; 27 } 28 while(!f[ans]&&ans)ans--; 29 printf("%d\n",ans); 30 return 0; 31 }
bzoj 2272: [Usaco2011 Feb]Cowlphabet 奶牛文字
dp。。。f[i][j][k]表示长度为(i+j)的单词中,有i个大写字母,j个小写字母,最后一个字母为k的方案数。
f[i][j][k]=sum{ f[i-1][j][k1] },(存在词素(k1,k),k为大写字母时)
或f[i][j][k]=sum{ f[i][j-1][k1] },(存在词素(k1,k),k为小写字母时)
大概是题目太水没人压常数嗯。。一开始卡常太sxbk连样例都过不了。。实在不敢调就重新写了个正常点的。。
取余的时候用减法,先把词素的俩字母连边就可以#1了= =
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=253; 7 const int modd=97654321; 8 int f[maxn][maxn][53]; 9 int mp[53][53],map[53],num[53]; 10 bool used[53]; 11 char x1,x2;int tmp1,tmp2,tot,upnum,lownum,tmp,to,ans; 12 int i,j,k,n,m,l,p,u; 13 bool up,low; 14 int main(){ 15 scanf("%d%d%d",&u,&l,&p); 16 for(i=1;i<=p;i++){ 17 for(x1=getchar();!isupper(x1)&&!islower(x1);x1=getchar()); 18 for(x2=getchar();!isupper(x2)&&!islower(x2);x2=getchar()); 19 if(x1>='a')tmp1=x1-'a'+26;else tmp1=x1-'A'; 20 if(x2>='a')tmp2=x2-'a'+26;else tmp2=x2-'A'; 21 if(!used[tmp1])map[++map[0]]=tmp1,used[tmp1]=1; 22 if(!used[tmp2])map[++map[0]]=tmp2,used[tmp2]=1; 23 mp[tmp1][++num[tmp1]]=tmp2; 24 } 25 sort(map+1,map+1+map[0]);for(i=1;i<=map[0];i++)if(map[i]>=26)break;upnum=i-1; 26 for(i=1;i<=upnum;i++)f[1][0][map[i]]=1;for(i=upnum+1;i<=map[0];i++)f[0][1][map[i]]=1; 27 // for(i=1;i<=upnum;i++)printf("%d ",map[i]);printf("\n"); 28 // for(i=upnum+1;i<=map[0];i++)printf("%d ",map[i]);printf("\n"); 29 for(i=0;i<=u;i++)for(j=0;j<=l;j++)if(i+j>=2){ 30 up=i>0;low=j>0; 31 if(up)for(tmp=1,k=map[1];tmp<=upnum;f[i][j][k]=ans,ans=0,k=map[++tmp]) 32 for(tmp1=1,to=mp[k][tmp1];tmp1<=num[k];to=mp[k][++tmp1]) 33 ans+=f[i-1][j][to],ans-=ans>=modd?modd:0; 34 if(low)for(tmp=upnum+1,k=map[tmp];tmp<=map[0];f[i][j][k]=ans,ans=0,k=map[++tmp]) 35 for(tmp1=1,to=mp[k][tmp1];tmp1<=num[k];to=mp[k][++tmp1]) 36 ans+=f[i][j-1][to],ans-=ans>=modd?modd:0; 37 } 38 for(i=0;i<52;i++)ans+=f[u][l][i],ans-=ans>=modd?modd:0; 39 printf("%d\n",ans); 40 return 0; 41 }
bzoj 1752: [Usaco2005 qua]Til the Cows Come Home
为何一道最短路的AC人数和AC率这么低= =
为何我spfa调优先队列和普通版的一样慢TAT
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=1001; 8 struct zs{ 9 int too,pre,dis; 10 }e[4001]; 11 struct poi{ 12 int pos,dis; 13 }; 14 int i,j,n,m,a,b,tot,c,size; 15 int last[maxn],dis[maxn]; 16 bool used[maxn]; 17 priority_queue<poi>q; 18 inline void insert(int a,int b,int c){ 19 e[++tot].too=b;e[tot].dis=c;e[tot].pre=last[a];last[a]=tot; 20 e[++tot].too=a;e[tot].dis=c;e[tot].pre=last[b];last[b]=tot; 21 } 22 bool operator <(poi a,poi b){return a.dis>b.dis;} 23 int ra;char rx; 24 inline int read(){ 25 rx=getchar();ra=0; 26 while(rx<'0'||rx>'9')rx=getchar(); 27 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 28 } 29 int main(){ 30 m=read();n=read();memset(dis,60,(n+1)<<2); 31 for(i=1;i<=m;i++)a=read(),b=read(),c=read(),insert(a,b,c); 32 dis[n]=0;size=1;q.push((poi){n,0});int pos,dist,to; 33 while(size&&!used[1]){ 34 while(size&&used[q.top().pos])q.pop(),size--; 35 pos=q.top().pos;dist=q.top().dis;used[pos]=1;if(pos==1)break; 36 for(i=last[pos],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(dis[to]>dist+e[i].dis) 37 dis[to]=dist+e[i].dis,q.push((poi){to,dis[to]}),size++; 38 } 39 printf("%d\n",dis[1]); 40 return 0; 41 }
bzoj 1738: [Usaco2005 mar]Ombrophobic Bovines 发抖的牛
一开始看成总时间最小以为是费用流。。。结果发现是二分答案+最大流判定
先用floyd把两两之间的最短路求出来,然后二分答案为mid,
建个二分图,S连每个点x,容量为开始前牛的数量,x'连T,容量为雨棚容量。两个点间的最短路径长如果<=mid的话就在新图中加条边,容量无穷。
检验新图中最大流是否为牛的总数。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int maxn=404; 7 struct zs{ 8 int too,pre,flow; 9 }e[82333]; 10 int last[maxn],dis[maxn],dl[maxn],num[maxn],mx[maxn]; 11 ll map[202][202]; 12 int i,j,k,n,m,a,b,c,s,t,sumnum,summx,tot; 13 ll l,r,mid; 14 int ra;char rx; 15 inline int read(){ 16 rx=getchar();ra=0; 17 while(rx<'0'||rx>'9')rx=getchar(); 18 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 19 } 20 inline bool bfs(){ 21 int l=0,r=1,i,now; 22 memset(dis,255,(t+1)<<2);dl[1]=s;dis[s]=0; 23 while(l<r){ 24 now=dl[++l]; 25 for(i=last[now];i;i=e[i].pre)if(e[i].flow&&dis[e[i].too]==-1) 26 dl[++r]=e[i].too,dis[e[i].too]=dis[now]+1; 27 } 28 return dis[t]!=-1; 29 } 30 int dfs(int x,int mx){ 31 if(x==t)return mx; 32 int i,used=0,w,to; 33 for(i=last[x],to=e[i].too;i;i=e[i].pre,to=e[i].too)if(e[i].flow&&dis[to]==dis[x]+1){ 34 w=dfs(to,min(mx-used,e[i].flow));if(w){ 35 e[i].flow-=w;e[i^1].flow+=w; 36 used+=w;if(used==mx)return mx; 37 } 38 } 39 dis[x]=-1;return used; 40 } 41 inline void insert(int a,int b,int c){ 42 // printf("%d-->%d:%d\n",a,b,c); 43 e[++tot].too=b;e[tot].flow=c;e[tot].pre=last[a];last[a]=tot; 44 e[++tot].too=a;e[tot].flow=0;e[tot].pre=last[b];last[b]=tot; 45 } 46 inline bool can(ll x){ 47 int i,j;//printf("! %lld\n",x); 48 for(i=1;i<=n;i++)insert(s,i,num[i]),insert(i+n,t,mx[i]); 49 for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(map[i][j]<=x)insert(i,j+n,1002333333); 50 int ans=0; 51 while(bfs())ans+=dfs(s,100002333); 52 return ans==sumnum; 53 } 54 int main(){ 55 n=read();m=read(); 56 for(i=0;i<=n;i++)memset(map[i],60,(n+1)<<3); 57 for(i=1;i<=n;i++)num[i]=read(),mx[i]=read(),map[i][i]=0,summx+=mx[i],sumnum+=num[i]; 58 if(summx<sumnum){puts("-1");return 0;} 59 for(i=1;i<=m;i++){ 60 a=read();b=read();c=read(); 61 if(c<map[a][b])map[a][b]=map[b][a]=c; 62 } 63 for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(map[i][k]+map[k][j]<map[i][j])map[i][j]=map[i][k]+map[k][j]; 64 65 for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(map[i][j]<map[0][0]&&map[i][j]>r)r=map[i][j];ll mxl=r; 66 l=0;r++;s=0;t=n+n+1;tot=1; 67 while(l<r){ 68 mid=(l+r)>>1; 69 if(can(mid))r=mid;else l=mid+1; 70 if(l<r)memset(last,0,(t+1)<<2),tot=1; 71 } 72 printf("%lld\n",l<=mxl?l:-1); 73 return 0; 74 }
bzoj 1986: [USACO2004 Dec] Dividing the Path 划区灌溉
dp+单调队列优化。。f[i]表示灌溉了1~i(全体右移一位从1开始。。)的最小喷灌器总数。设各草区左右端点为l[],r[]
f[i]=min{ f[j] }+1,(1、i和j为偶数,2*A<=i-j<=2*B;2、不存在k,使l[k]<=j,r[k]>j)
第二个转移条件也就是i和j都不存在于任意[ l[i],r[i]-1 ]中。
调个优先队列就行了。。每次算f[i]前把f[i-A*2]入队。时间复杂度O(n)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int maxn=1000033; 6 int dl[maxn],f[maxn]; 7 int map[maxn]; 8 int i,j,n,m,len,a,b,l,r,A,B,now; 9 int ra;char rx; 10 inline int read(){ 11 rx=getchar();ra=0; 12 while(rx<'0'||rx>'9')rx=getchar(); 13 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 14 } 15 int main(){ 16 n=read();len=read();A=read();B=read(); 17 memset(f,60,(min(len,A*2)+1)<<2); 18 for(i=1;i<=n;i++) 19 a=read(),b=read(),map[min(a+1,b)]++,map[b]--; 20 int inf=600233333; 21 l=1;r=0; 22 f[0]=0; 23 a=A*2-B*2;b=0;for(i=1;i<A*2;i++)now+=map[i]; 24 for(i=A<<1;i<=len;i+=2,a+=2,b+=2){ 25 while(l<=r&&f[dl[r]]>=f[b])r--;dl[++r]=b;if(f[dl[r]]>=inf)r--; 26 now+=map[i]+map[i-1];if(now){f[i]=inf;continue;} 27 while(l<=r&&dl[l]<a)l++; 28 f[i]=(l<=r&&f[dl[l]]<inf)?(f[dl[l]]+1):inf; 29 } 30 printf("%d\n",f[len]<inf?f[len]:-1); 31 return 0; 32 }
bzoj 1605: [Usaco2008 Open]Crisis on the Farm 牧场危机
DP。。先预处理出val[i][j],表示使各牛群横纵坐标分别增加了i和j后能救几头牛(只算最后一次吹哨救的)。。
f[i][j][k]表示吹了k次哨子,使各牛群横纵坐标分别增加了i和j能救的最多奶牛数。
f[i][j][k]=max{ f[i1][j1][k-1] }+val[i][j]。(点(i1,j1)与点(i,j)相邻)。
觉得直接dp的话冗余状态略蛋疼就借(chao)鉴(xi)题解代码写了记忆化搜索= =输出方案的话记录下f[i][j][k]是从哪里转移来的
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 using namespace std; 6 const int maxn=61; 7 const int maxstep=31; 8 const int xx[5]={0,1,0,0,-1},yy[5]={0,0,1,-1,0}; 9 int next[maxn][maxn][maxstep],f[maxn][maxn][maxstep],val[maxn][maxn]; 10 int x[1001],y[1001],gx[1001],gy[1001]; 11 int i,j,k,n,m,a,b,deltax,deltay; 12 char map[5]={0,'E','N','S','W'}; 13 int ra;char rx; 14 inline int read(){ 15 rx=getchar();ra=0; 16 while(rx<'0'||rx>'9')rx=getchar(); 17 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 18 } 19 //f[i][j][k]表示横纵坐标增加了i和j,剩k次吹哨机会时的最大值。 20 int dfs(int dx,int dy,int rest){ 21 if(rest<=0)return val[dx][dy]; 22 if(next[dx][dy][rest])return f[dx][dy][rest]; 23 next[dx][dy][rest]=1;f[dx][dy][rest]=dfs(dx+xx[1],dy+yy[1],rest-1); 24 for(int i=2;i<=4;i++) 25 if(dfs(dx+xx[i],dy+yy[i],rest-1)>f[dx][dy][rest]) 26 f[dx][dy][rest]=dfs(dx+xx[i],dy+yy[i],rest-1),next[dx][dy][rest]=i; 27 f[dx][dy][rest]+=val[dx][dy];//printf("%d %d %d %d %d\n",dx,dy,rest,f[dx][dy][rest],next[dx][dy][rest]); 28 return f[dx][dy][rest]; 29 } 30 int main(){ 31 n=read();m=read();k=read(); 32 for(i=1;i<=n;i++)x[i]=read(),y[i]=read(); 33 for(i=1;i<=m;i++){ 34 a=read();b=read(); 35 for(j=1,deltax=abs(x[j]-a),deltay=abs(y[j]-b);j<=n;deltax=abs(x[++j]-a),deltay=abs(y[j]-b)) 36 if(deltax+deltay<=k)val[a-x[j]+30][b-y[j]+30]++; 37 } 38 printf("%d\n",dfs(30,30,k)-val[30][30]); 39 int nowx=30,nowy=30; 40 for(;k;k--){ 41 putchar(map[next[nowx][nowy][k]]); 42 if(next[nowx][nowy][k]<2||next[nowx][nowy][k]>3)nowx+=xx[next[nowx][nowy][k]];else nowy+=yy[next[nowx][nowy][k]]; 43 } 44 putchar('\n'); 45 return 0; 46 }