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