「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");
}

posted @ 2019-11-13 19:41  蒟蒻--lichenxi  阅读(218)  评论(0编辑  收藏  举报