【GDOI2017模拟8.20】准备食物2
2016-09-21 20:40
309 查看
Description
此生无悔入东方,来世愿生幻想乡!我没入教,WorldWide_D(出题人)入了
古明地觉拥有的第三只眼,可以读取别人的内心想法,于是无论是妖怪,还是怨灵都为之感到恐惧,因此觉被人厌恶。
然而,觉却因能读心而深受那些无法开口的动物喜爱。整个地灵殿到处都是她的宠物(如火焰猫、地狱鸦)。
宠物多了,准备食物是个麻烦的问题。现在觉有m种食物,第i种食物有a[i]份。觉要为n个宠物按编号顺序分配食物,每个宠物需要1份食物。
觉通过读心,得出了每个宠物吃了每种食物后的喜悦值。觉还发现,对于一些宠物,假设它的编号为i,如果在它之前分配到食物的宠物中,超过s[i]个被分配了第num[i]种食物,那么它会密谋一些反动的事情(像间歇泉异变之类的)。
觉不允许反动的事情发生,也希望所有宠物获得的喜悦值最大,以让它们更好地为她管理地灵殿和灼热地狱的事务。现在她想知道,在不会反动的情况下,能获得的最大喜悦值为多少。如果无论怎样分配都会出现反动,或者食物不够分配,只要输出“Warning!”(不含引号)
1≤T≤5,1≤a[i],n≤200 1≤m≤100 -1≤s[i]≤n -1≤num[i]≤m且num[i]≠0 0≤v[i][j]≤100000
Solution
东方教徒真可怕.jpg像这种一眼看过去有一堆奇奇怪怪的限制的,没有什么其他做法的题,当然是网络流啦!
显然最大费用最大流,无解最大流 < n。
所以我们只需要知道如何考虑限制。每个食物的总数也可以看成一个限制方便处理。
我们把限制食物相同的限制拉出来,按s排个序。
显然如果一个i,j(i < j),且si > sj那么限制i就是没有用的,扔掉。
现在我们就得到了一个递增的i和s都递增的限制,那么我们可以看成对一堆前缀进行限制。
显然相邻两个限制相当于限制一个区间最多选择的数量。
这样子我们就把限制划分成了许多不交的区间。
那么对于每一个限制的区间我们开一个点,从它所限制的点向它流容量为1,费用为v的边。然后这个点向这个食物所代表的点连容量为它的限制的边。
当然,因为区间直接相邻,我们也可以从上一个区间向它连上一个区间的限制。
最后一个区间向汇点连总限制。
这样就可以处理限制了。
忘记了zkw写最大费用最大流会出事啊
Code
#include<cstdio> #include<cstring> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define rep(i,a) for(int i=last[a];i;i=next[i]) #define N 505 #define M 51005 using namespace std; const int inf=0x7fffffff,mx=200000; struct note{int s,num,id;}a ; bool cmp(note x,note y) {return x.num<y.num||x.num==y.num&&x.s<y.s;} int ty,n,m,l,tot,S,T,id,ans,flow,value ,dis ,bz ; int last ,next[M],t[M],v[M],f[M]; void add(int x,int y,int z,int c) { t[++l]=y;f[l]=z;v[l]=c;next[l]=last[x];last[x]=l; t[++l]=x;f[l]=0;v[l]=-c;next[l]=last[y];last[y]=l; } void link(int l,int r) { int p=0;bool pd=0;if (a[l].num<0) return; fo(i,l,r) { bool bz=0; fo(j,l,i-1) if (a[j].id>a[i].id) {bz=1;continue;} if (bz) continue;tot++; fo(j,a[p].id,a[i].id-1) add(j,tot,1,mx-value[j][a[l].num]); if (pd) add(tot-1,tot,a[p].s,0);p=i;pd=1; } if (pd) add(tot,T,a[p].s,0); } int aug(int x,int y,int z) { bz[x]=id; if (x==T) {ans+=y*z;flow+=y;return y;} rep(i,x) if (f[i]&&bz[t[i]]!=id&&dis[x]==dis[t[i]]+v[i]) { int k=aug(t[i],min(y,f[i]),z+v[i]); if (k) {f[i]-=k;f[i^1]+=k;return k;} } return 0; } bool find() { int k=inf; fo(i,S,tot) if (bz[i]==id) rep(j,i) if (bz[t[j]]!=id&&f[j]) k=min(k,dis[t[j]]-dis[i]+v[j]); if (k==inf) return 0; fo(i,S,tot) if (bz[i]==id) dis[i]+=k; return 1; } int main() { for(scanf("%d",&ty);ty;ty--) { l=1;memset(last,0,sizeof(last));S=id=ans=flow=0;T=tot=n+1; scanf("%d%d",&n,&m); fo(i,1,n) fo(j,1,m) scanf("%d",&value[i][j]); fo(i,1,n) scanf("%d%d",&a[i].s,&a[i].num),a[i].id=i; fo(i,1,m) scanf("%d",&a[i+n].s),a[i+n].num=i,a[i+n].id=n+1; sort(a+1,a+n+m+1,cmp); int la=1;a[0].id=1;fo(i,1,n) add(S,i,1,0); fo(i,1,n+m) if (a[i].num!=a[i-1].num) link(la,i-1),la=i; link(la,n+m); memset(dis,0,sizeof(dis));memset(bz,0,sizeof(bz)); do {id++;while (aug(S,inf,0)) id++;} while (find()); if (flow==n) printf("%d\n",n*mx-ans); else printf("Warning!\n"); } }
相关文章推荐
- [jzoj4598]【NOIP2016模拟7.9】准备食物
- 5322. 【GDOI2017模拟8.21】小朋友 状压dp
- 群体智能算法-黏菌寻找食物最优路线行为模拟
- GDOI2017第三轮模拟总结
- 【GDOI2017第三轮模拟day2】树的难题(点剖,树状数组)
- 【GDOI2017模拟一试4.11】腐女的生日
- 【jzoj4982】【GDOI2017模拟2.23】【加密】【sam】
- 准备食物(trie) 题解+代码
- 康康准备了 M 斤的食物, 准备跟舍长交换哲学之宝 ♂
- GDOI2017模拟2.16
- 【GDOI2017模拟11.3】永恒的契约
- 【GDOI2017模拟11.3】永恒的契约
- 【GDOI2017第二轮模拟day2】中位数
- JZOJ 3789. 【NOI2015模拟8.20】编辑器
- 【NOIP2012模拟8.20】Memory
- 准备食物
- 准备食物
- 【jzoj4598】【准备食物】【字典树】
- [准备篇6]VMWare搭建Openstack——使用VMnet的方式模拟多网卡的OpenStack环境配置(2)
- 模拟实现WPF的依赖属性及绑定通知机制(2)--依赖对象的准备