「BalticOI 2015」拔河

LOj

显然题目可以转化为一个二分图。

先找一下是否存在度数为\(0\)的点,存在则无解、

然后把度数为\(1\)的点拎出来,这些点的选择是唯一的。

这些可以用拓扑排序处理一下。

接下来就剩下了一堆环。

然后因为不能选择相邻的边,一个环内有两种选法,设为\(A\)\(B\)

考虑先选出和比较小的那个,假设是\(A\),然后对\(B-A\)背包。

因为权值范围比较小,所以可以把\(01\)背包转化为多重背包,然后就可以二进制拆分+\(bitset\)优化。

然后只要判断\([-k,k]\)能否凑出来就行了。

复杂度貌似是\(\frac{k\sqrt{k}\log k}{64}\)

代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
#include<set>
#include<bitset>
#include<map>
using namespace std;
#define rg register
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=2e5+10;queue<int>qq;
map<int,int>mp;
int sum,n,m,k,w,in[maxn],q[maxn],tot,s[maxn],t,num[maxn];bool vis[maxn];
struct oo{int l,r,x;}a[maxn];
int cnt,pre[maxn*2],nxt[maxn*2],h[maxn],v[maxn*2];
bitset<maxn*30>f;
void add(int x,int y,int z){
    pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,v[cnt]=z;
    pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt,v[cnt]=z;
}
void topsort(){
    for(rg int i=1;i<=n*2;i++)if(in[i]==1)qq.push(i);
    while(!qq.empty()){
    int x=qq.front();qq.pop();vis[x]=1;
    for(rg int i=h[x];i;i=nxt[i])
        if(!vis[pre[i]]){
        in[pre[i]]--;
        if(in[pre[i]]==1)qq.push(pre[i]);
        if(x>n)sum+=v[i];
    }
    }
    for(rg int i=1;i<=n*2;i++)
    if(!vis[i]){
        tot=0;int l=i;
        while(!vis[l]){
        vis[l]=1;
        for(rg int j=h[l];j;j=nxt[j])if(!vis[pre[j]])l=pre[j],q[++tot]=v[j];
        }
        int x=0,y=0;
        for(rg int j=1;j<=tot;j+=2)x+=q[j];
        for(rg int j=2;j<=tot;j+=2)y+=q[j];
        if(x>y)swap(x,y);sum+=x;
        if(!mp[y-x])mp[y-x]=++t,s[t]=y-x;
        num[mp[y-x]]++;
    }
}
int main(){
    read(n),read(k);w=n;n<<=1;
    for(rg int i=1;i<=n;i++){
    read(a[i].l),read(a[i].r),read(a[i].x);a[i].l+=n,a[i].r+=n+w;
    in[a[i].l]++,in[a[i].r]++;in[i]=2;
    add(i,a[i].l,a[i].x),add(i,a[i].r,-a[i].x);
    }
    for(rg int i=n+1;i<=n*2;i++)
    if(!in[i])return puts("NO"),0;
    topsort();f[0]=1;
    for(rg int i=1;i<=t;i++){
    for(rg int j=1;j<num[i];j<<=1)
        f|=f<<(s[i]*j),num[i]-=j;
    f|=f<<(s[i]*num[i]);
    }
    for(rg int i=-k;i<=k;i++)
    if(sum<=i&&f[i-sum])return puts("YES"),0;
    puts("NO");
}

猜你喜欢

转载自www.cnblogs.com/lcxer/p/11852114.html