【GDOI2017模拟8.20】准备食物2

来源:互联网 发布:mysql 修复数据表 编辑:程序博客网 时间:2024/04/28 07:52

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 51005using namespace std;const int inf=0x7fffffff,mx=200000;struct note{int s,num,id;}a[N];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[N][N],dis[N],bz[N];int last[N],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");    }}
0 0
原创粉丝点击