题目
n(n<=1e5)种菜,m(m<=2e5)个朋友,第i种菜有wi(0<=wi<=1e6)盘
每个朋友有两盘菜的喜好,x或y,他会随机挑至少一盘当前有的菜然后吃掉
可以理解为有两盘吃两盘,有一盘只吃一盘,
特别的,如果都没有,他会把主人吃掉23333
请主人合理地分配朋友就餐顺序,使得主人不会被吃掉
如果一定会被吃掉,输出DEAD;否则输出ALIVE,及m位朋友的就餐顺序
思路来源
B站博主zbw讲解、粉兔代码
题解
对n种菜分开考虑,如果存在第i种菜被集合S的朋友喜爱且均够吃,
即喜爱i种菜的人有bi个人,当前有ai盘菜,ai>=bi
那么可以把这bi个人留到最后来分第i种菜,并撤掉这盘菜,
这样这bi个人都分到了菜,且不会影响到不吃这盘菜的人,
并使其他菜有所剩余,使方案可能更优
特别地,如果对于所有i,ai<bi,则不妨设排列中最后一个人喜好为(x,y)
由于ax<=bx-1,ay<=by-1,前n-1个人的需求就会把x、y这两种菜吃完
导致最后一个人没菜吃,从而无解
可以把菜加到删的次序的末尾并更新其他菜这种操作,
视为类似拓扑排序删掉入度为0并更新其他入度的操作,
于是构造好这个删菜倒序序列后,从后往前遍历,
每个客人保证在自己只剩一盘喜欢的菜时,再吃掉这盘
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
int n,m,x,y,a[N],b[N],del[N];
vector<P>g[N];
int q[N],r;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for(int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
b[x]++;b[y]++;
g[x].pb(P(y,i));
g[y].pb(P(x,i));
}
for(int i=1;i<=n;++i){
if(a[i]>=b[i]){
q[r++]=i;
}
}
for(int l=0;l<r;++l){
int x=q[l];
del[x]=l;
for(P y:g[x]){
int oth=y.fi;
if(a[oth]>=b[oth])continue;
if(--b[oth]==a[oth]){
q[r++]=oth;
}
}
}
if(r!=n){
puts("DEAD");
return 0;
}
puts("ALIVE");
for(int i=n-1;i>=0;--i){
int x=q[i];
for(P y:g[x]){
if(del[y.fi]>i){
printf("%d ",y.se);
}
}
}
return 0;
}