您的位置:首页 > 其它

【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");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: