Codeforces 808F Card Game

Digital collectible card games have become very popular recently. So Vova decided to try one of these.

Vova has n cards in his collection. Each of these cards is characterised by its power pi, magic number ci and level li. Vova wants to build a deck with total power not less than k, but magic numbers may not allow him to do so — Vova can’t place two cards in a deck if the sum of the magic numbers written on these cards is a prime number. Also Vova cannot use a card if its level is greater than the level of Vova’s character.

At the moment Vova’s character’s level is 1. Help Vova to determine the minimum level he needs to reach in order to build a deck with the required total power.

Input
The first line contains two integers n and k (1 ≤ n ≤ 100, 1 ≤ k ≤ 100000).

Then n lines follow, each of these lines contains three numbers that represent the corresponding card: pi, ci and li (1 ≤ pi ≤ 1000, 1 ≤ ci ≤ 100000, 1 ≤ li ≤ n).

Output
If Vova won’t be able to build a deck with required power, print  - 1. Otherwise print the minimum level Vova has to reach in order to build a deck.

Example
Input
5 8
5 5 1
1 5 4
4 6 3
1 12 4
3 12 1
Output
4
Input
3 7
4 4 1
5 8 2
5 3 3
Output
2

题意

大概的解释一下.
现有一些卡,每张卡有火力值,时长和等级,Vova不能选比自己等级高的卡牌,并且Vova不能同时选两张时长之和为质数的卡牌.问在获得不小于k的火力值的前提下,Vova最少应该是多少等级?

题解

很明显要二分等级,关键是怎么check.
Vova说火力总和不能小于k,我们肯定要当前等级下的最优选取方案,其实就是最大点权独立集.
我们发现只有奇数加偶数才会是质数(有一个例外,等下说),所以我们把奇数归为一类,偶数归为一类,某奇数与某偶数加起来是质数就连一条边,容量为inf(好吧没错我要跑网络流,等下说为什么).建一个虚拟源点和虚拟汇点,奇数与源点连容量为他火力的边。这是个充满矛盾的图,因为只有和为质数即矛盾的点我们才连边.偶数往汇点连容量为他火力的边.相当于就是跑一边最大流即跑出最小割,可以理解为就是花最小的代价使矛盾的边断开,因为我们要求是不能有和为质数的,即有矛盾的.这个最小割就是使矛盾消除(矛盾的边割开使不连通,这里没理解要去看一下最小割的意义)的最小代价,即为了满足限制损失的最小火力,你原本小于当前二分的等级的卡牌的总价值减去 消去矛盾的最小代价(损失)就是我们最大能获得到的火力.这个最大收益>=k就check 返回true;
Tips:
1.1+1=2,两奇数相加为质数.所以与我们建图不符.但是我们只需要在所有时长为1的卡牌里面选取最大的火力就可以了.
2.在程序里为了满足<=mid,即当前二分的等级,我们只需要在跑dinic的时候发现>mid就不去跑就可以了.Tips1中我们也只需要标记所选择的那个时长为1的卡牌,dinic中发现时长为1但是没有标记的不去跑,continue就可以了.
3.综上1,2,我们只用建1次图

#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=10000;
const int inf=0x3f3f3f3f;
int h[N],num=1,k;
int P[N],T[N],L[N];
int prime[1500],ptot,n,sum,all,pos,dis[N],s,t,cur[N];
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    register int x=0,f=1;
    register char ch=nc();
    while(ch<'0'||ch>'9'){if(ch=='-')f*=-1;ch=nc();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    return f*x;
}
struct Edge{
    int v,next,c,pr;
};
Edge e[100*N];
void adde(int i,int j,int c){
    e[++num].v=j,e[num].next=h[i],e[num].c=e[num].pr=c,h[i]=num;
}
void add(int u,int v,int w){
    adde(u,v,w);
    adde(v,u,0);
}
inline bool bfs(int mid,int pos){
    queue<int> q;
    memset(dis,0,sizeof(dis));
    q.push(s);
    dis[s]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=h[u];i;i=e[i].next){
            int v=e[i].v;
            if(T[v]==1&&v!=pos)continue;
            if(e[i].c&&!dis[v]&&L[v]<=mid){
               dis[v]=dis[u]+1;
               q.push(v);
            }
        }
    }
    return dis[t];
}
int dfs(int u,int a,int mid,int pos){
    if(u==t||a==0)return a;
    int flow=0,f;
    for(int i=cur[u];i;i=cur[u]=e[i].next){
        int v=e[i].v;
        if(T[v]==1&&pos!=v)continue;
        if(L[v]<=mid&&dis[v]==dis[u]+1){
            int x=min(e[i].c,a);
            f=dfs(v,x,mid,pos);
            e[i].c-=f;
            e[i^1].c+=f;
            flow+=f;
            a-=f;
            if(a==0)return flow;
        }
    }
    if(!flow)dis[u]=-1;
    return flow;
}
void init(){
    prime[++ptot]=2;
    for(int i=3;i<=10000;i++){
        int j;
        for(j=2;j*j<=i;j++)
        if(i%j==0)break;
        if(j*j>i)prime[++ptot]=i;
    }
}
bool isprime(int x){
    for(int i=1;prime[i]*prime[i]<=x;i++)
    if(x%prime[i]==0)return false;
    return true;
}
void pre(){
    for(int i=1;i<=n;i++){
         if(T[i]&1)
        for(int j=1;j<=n;j++){
            if(!(T[j]&1))
             if(isprime(T[i]+T[j]))add(i,j,inf);
        }
    }
}
bool check(int mid){
    all=0;int maxx=0;
    for(int i=1;i<=n;i++){
        if(L[i]<=mid&&T[i]!=1)all+=P[i];
        if(L[i]<=mid&&T[i]==1){
            if(P[i]>maxx){
                maxx=P[i];
                pos=i;
            }
        }
    }
    all+=maxx;
    int flow=0,ans=0;
    for(int i=2;i<=num;i++) e[i].c=e[i].pr;
    while(bfs(mid,pos)) {for(int i=s;i<=t;i++)cur[i]=h[i];flow+=dfs(s,inf,mid,pos);}
    return all-flow>=k;
}
int main(){
    init();
    L[0]=-1;L[1200]=-1;
    n=read(),k=read();
    s=0,t=2*n+1;
    int st=inf,ed=0;
    for(int i=1;i<=n;i++){
        P[i]=read(),T[i]=read(),L[i]=read();
        st=min(st,L[i]),ed=max(ed,L[i]);
        if(T[i]&1)add(s,i,P[i]);
        else add(i,t,P[i]);
    }
    pre();
    int lf=st,rg=ed,ans=-1;
    while(lf<=rg){
       int mid=(lf+rg)>>1;
       if(check(mid)) ans=mid,rg=mid-1;
       else lf=mid+1;
    }
    if(ans!=-1){
    while(check(ans-1)) ans--;
    printf("%d\n",ans);
    }
    else printf("-1\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值