题意:给出一组卡牌(100张),卡牌有三个属性值:power,c,level,其中c和level是用来限制的,power是目标值。
具体的限制规则是:只有level小于等于玩家的playerlevel的卡牌才可以使用,任意两张c之和为prime的卡牌不能同时使用。
求出一个人物的最小等级,使得可以找到一个方案使得选用的卡牌的power值累计大于等于k。
这个题很容易想到二分playerlevel,然后关键在于已知playerlevel的时候,如何计算可以得到的最大的power。
Solution是使用maxFlow,这算是一个网络流建图题。
首先可以预处理出所有相互冲突的卡牌。如果在他们之间连接一条边,就构成了一个冲突网络。显而易见:一条边的两个端点一定是一个奇数一个偶数。
那么可以把节点归为偶数的点和奇数的点。现在的问题是,要去掉一些点,使得网络为空。下面讲解建图技巧。
构造一个偶数超源102点,一个奇数超汇101点,102点会和每一个偶数点连一条指向偶数点的边,边的容量是偶数点的power,101点会和每个技术点连一条指向101点的边,容量为奇数点的power。然后再冲突的奇偶点之间连接从偶数点指向奇数点的边,容量是INF。建图完成。
那么首先这张初始图上的总power值就是各个奇偶点的power之和,然后下面开始“去掉一些点,使得不存在冲突关系”。
从101到102跑一次maxFlow可以得到这个冲突网络的最大流,也就是最小割。那么考虑这个最小割的实际意义:划定一个边集,这个边集可以将冲突网络分成不想连的两部分,且我这个边集的边权之和最小,那么显然,我们的那些冲突边不可能出现在这里边, 因为他们都是INF,因此,显而易见,这个最小割当然就是我们要得到的答案。因为割掉这些边之后,我们就不存在一条经过102-偶数点-冲突边-奇数点-101的流了,也就是说,要么奇数点-101的边被割了,要么偶数点-102的边被割了,要么同时被割了(可能存在多重冲突情况,需要两个点都割),这样就能保证网络变得合法而不冲突,而且最小割也保证了这是代价最小的割,因此就是我们要的答案。
格外要注意1的情形,1虽然属于奇数点,但是考虑出现多个1的时候,这些1都是“奇数”,但是任意两个1之间也存在冲突关系,但是上面我们只考虑了从超源102-超汇101之间要没有带冲突的流,而没有考虑1-1之间这个冲突边,所以不能同时出现多个1在我们的图中,这个也很好处理,显然,在我们的卡组中,至多会有一张c=1的牌,所以建图的时候,先遍历一次得到我可以使用的power最大的那个c=1的牌,而把剩下其他的c=1的牌都舍弃掉就可以了。
注:dinic不需要反向弧
AC代码(需要改动):
#include<bits/stdc++.h>
using namespace std;
#define MAXN 105
#define MAXM 100005
int first[MAXN],nxt[MAXM],dis[MAXM],cont[MAXM];
int m,n,k,s,t,tot;
int p[MAXN],c[MAXN],l[MAXN];
bool prm[200005];
int deep[MAXN];
int que[MAXN*10];
bool hasEdge[MAXN][MAXN];
int maxone=-1;
int maxoneindex=-1;
bool bfs(){
int l=0,r=1;
que[1]=s;
memset(deep,0,sizeof(deep));
while (l<r){
l++;
int q = que[l];
int tt=first[q];
while (tt!=-1){
if (dis[tt]!=s&&cont[tt]>0&&deep[dis[tt]]==0){
deep[dis[tt]]=deep[q]+1;
r++;
que[r]=dis[tt];
}
tt=nxt[tt];
}
}
if (deep[t]==0){
return false;
}else{
return true;
}
}
int dfs(int now,int limit){
if (now==t||limit==0){
return limit;
}
if (limit<=0){
return 0;
}
int flow=0,f;
int tt=first[now];
while (tt!=-1){
if (deep[dis[tt]]-deep[now]==1){
int temp = dfs(dis[tt],min(limit,cont[tt]));
cont[tt]-=temp;
cont[tt^1]+=temp;
limit-=temp;
flow+=temp;
if (limit==0){
break;
}
}
tt=nxt[tt];
}
return flow;
}
int maxFlow(int least){
tot=-1;
for (int i=0;i<103;i++){
first[i]=-1;
}
for (int i=0;i<n;i++){
for (int j=i+1;j<n;j++){
if (c[i]==1&&i!=maxoneindex){
continue;
}
if (c[j]==1&&j!=maxoneindex){
continue;
}
if (hasEdge[i][j]&&l[i]<=least&&l[j]<=least){
int ii,jj;
if (c[i]&1){
ii=i;
jj=j;
}else{
ii=j;
jj=i;
}
tot++;
cont[tot]=2147483647;
dis[tot]=jj;
nxt[tot]=first[ii];
first[ii]=tot;
tot++;
cont[tot]=0;
dis[tot]=ii;
nxt[tot]=first[jj];
first[jj]=tot;
}
}
}
int sum =0;
for (int i=0;i<n;i++){
if (c[i]==1&&i!=maxoneindex){
continue;
}
if (l[i]<=least){
sum+=p[i];
if (c[i]&1){
tot++;
cont[tot]=p[i];
dis[tot]=i;
nxt[tot]=first[101];
first[101]=tot;
tot++;
cont[tot]=p[i];
dis[tot]=101;
nxt[tot]=first[i];
first[i]=tot;
}else{
tot++;
cont[tot]=p[i];
dis[tot]=i;
nxt[tot]=first[102];
first[102]=tot;
tot++;
cont[tot]=p[i];
dis[tot]=102;
nxt[tot]=first[i];
first[i]=tot;
}
}
}
int ans =0;
while (bfs()){
ans+=dfs(s,2147483647);
}
return sum-ans;
}
int main(){
memset(prm,true,sizeof(prm));
prm[0]=prm[1]=false;
for (int i=2;i<200000;i++){
if (prm[i]){
int tmp=i<<1;
while (tmp<200000){
prm[tmp]=false;
tmp+=i;
}
}
}
cin>>n>>k;
memset(hasEdge,false,sizeof(hasEdge));
for (int i=0;i<n;i++){
cin>>p[i]>>c[i]>>l[i];
if (c[i]==1&&p[i]>maxone){
maxone=p[i];
maxoneindex=i;
}
for (int j=0;j<i;j++){
if (prm[c[i]+c[j]]){
hasEdge[i][j]=hasEdge[j][i]=true;
}
}
}
s=101;
t=102;
if (maxFlow(101)<k){
cout<<"-1"<<endl;
return 0;
}
int l=0;
int r=101;
while (r-l>1){
int mid =(l+r)>>1;
if (maxFlow(mid)>=k){
r=mid;
}else{
l=mid;
}
}
if (maxFlow(l)>=k){
cout<<l<<endl;
}else{
cout<<r<<endl;
}
return 0;
}