CF808F Card Game 网络流

首先答案肯定有单调性,先二分出答案

然后如何保证c[i]+c[j]不为质数

如果同奇偶的话,除了1和1,其他必不为质数,否则可以for循环暴力判一下

所以如果是质数,必定为一奇一偶,这两点不能同时选

启发了我们可以把点分开来,奇数在一边,偶数在一边,在不能同时选的两点间连inf的边(因为inf不能被割掉,所以肯定是两边的点有一个被割掉)

然后就变成了很经典的,"假设全部获利,割掉表示不要,求最小化损失也就是最大化获利"的模型了

复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=INT_MAX;
const int maxn=10000;
struct lys{
    ll from,to,nxt,c;
}e[maxn*4];
ll cnt=1,n,m,k,s,t,dep[maxn],head[maxn],val[maxn],cur[maxn];
void add(int from,int to,ll c)
{
    cnt++;
    e[cnt].from=from;e[cnt].to=to;e[cnt].nxt=head[from];head[from]=cnt;val[cnt]=c;
}

bool  bfs()
{   queue<int>q;
    memset(dep,0,sizeof(dep));
    q.push(s);
    dep[s]=1;
    cur[s]=head[s];
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        {
            int to=e[i].to;
            if(val[i]&&!dep[to])
            {
                dep[to]=dep[u]+1;
                cur[to]=head[to];
                q.push(to);
            }
        }
    }
    return dep[t];    
}
ll dfs(int u,ll in)
{
    if(u==t) 
    return in;
    ll out=0;
    for(int i=cur[u];i&&in;i=e[i].nxt)
    {
      cur[u]=i;
      int to=e[i].to;
      if(val[i]&&dep[to]==dep[u]+1)
      {
          ll res=dfs(to,min(val[i],in));
          val[i]-=res;
          val[i^1]+=res;
          in-=res;
          out+=res;
      }    
    }
    
    if(!out) 
      dep[u]=0;
    return out;
}
int prime[int(2e5)+5], pri_cnt=0 ;
bool vis[int(2e5)+5];        //0表示是素数,1表示是合数

void get_prime(int n) {
    for (int i = 2; i <= n; i++) {        //这里直接跳过1
        if (!vis[i])
            prime[pri_cnt++] = i;    //记录素数
        for (int j = 0; j < pri_cnt && i * prime[j] <= n; j++) {                                                
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0)    //避免重复筛选
                break;
        }
    }
}
int p[maxn],c[maxn],l[maxn],use[maxn];
bool valid(int level){
    memset(use,0,sizeof(use));
    for(int i=1;i<=n;i++) if(l[i]<=level) use[i]=1;
    // deal with "1"
    int mx=-1;
    for(int i=1;i<=n;i++) if(c[i]==1&&use[i]) mx=max(mx,p[i]);
    if(mx!=-1){
        for(int i=1;i<=n;i++) if(c[i]==1&&use[i]) use[i]=0;
        bool find=false;
        for(int i=1;i<=n&&!find;i++) {
           if(c[i]==1&&p[i]==mx&&l[i]<=level) use[i]=1,find=true;
        }
    }
    cnt=1;
    memset(head,0,sizeof(head));
    memset(val,0,sizeof(val));
    memset(cur,0,sizeof(cur));
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++){
         if(i==j) continue;
         if(!use[i]||!use[j]) continue;
         if(!vis[c[i]+c[j]]) {
            // 左odd 右even 
            if(c[i]%2){
                   add(i,j,inf);
                   add(j,i,0);
            }
            else {
               add(j,i,inf);
               add(i,j,0);
            }    
        }
    }
    s=0,t=n+1;
    ll sum=0;
    for(int i=1;i<=n;i++){
        if(!use[i]) continue;
        sum+=p[i];
        if(c[i]%2){
            add(s,i,p[i]);
            add(i,s,0);
        }
        else {
            add(i,t,p[i]);
            add(t,i,0);
        }
    }
    ll ans=0;
    while(bfs())     ans+=dfs(s,1e18);
    if(sum-ans>=k) return true;
    else return false;
}
int main()
{   //freopen("lys.in","r",stdin);
    get_prime(2e5);
    cin>>n>>k;
    int odd=0,even=0;
    for(int i=1;i<=n;i++)
        cin>>p[i]>>c[i]>>l[i];
    int l=1,r=n,out=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(valid(mid)){
            out=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    cout<<out;
}
复制代码

 

posted @   liyishui  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示