[bzoj4644][线性基][线段树分治]经典傻逼题

39 篇文章 0 订阅
4 篇文章 0 订阅

Description

这是一道经典傻逼题,对经典题很熟悉的人也不要激动,希望大家不要傻逼。 考虑一张N个点的带权无向图,点的编号为1到N。 对于图中的任意一个点集
(可以为空或者全集),所有恰好有一个端点在这个点集中的边组成的集合被称 为割。 一个割的权值被定义为所有在这个割上的边的异或和。
一开始这张图是空图, 现在,考虑给这张无向图不断的加边, 加入每条边之 后,你都要求出当前权值最大的割的权值, 注意加入的边永远都不会消失。
Input 输入的第一行包括一个数ID表示数据编号, 如第一组数据中的ID = 1。注意 样例数据中的ID = 0。
接下来的第一行包括两个整数N,M表示图的点数和总共加的边。 接下来M行,每行三个正整数x,y,w表示在点x和点y之间加入一条权值为w的边。
注意x和y可能相同,两条不同的边也可能连接了同一对点。 此外, w将以二进制形式从高位向低位给出,比如, 6 = 110(2),因此如果边
权为 6,那么w将会是110。 1 ≤ N≤ 500, 1 ≤ M ≤ 1000, 0 ≤ L < 1000, 1 ≤ x,y≤ N

Output

输出M行,按顺序输出每一条边加入之后权值最大的割的权值。 同样,你也要以二进制形式输出,形式和输入格式中描述的形式一样。

Sample Input

0 3

6

1 2 1

1 2 1

3 3 111

1 3 101101

1 2 1011

2 3 111011

Sample Output

1 0 0

101101

101101

110000

HINT

前三条边加入之后的答案较为显然,考虑后三条边,加入第六条边之前, 考

虑点集{1,2},它对应的割只有第四条边, 因此答案就是第四条边的权值,考虑加

入最后一条边以后的情况,此时点集{1,2}对应的割变成了第四条边和第六条边组

成的集合,权值也发生了相应的改变。 点集{2}对应的割是第五条边和第六条边

组成的集合, 可以证明这就是权值最大的割,权值为1011(2) ⊕ 111011(2) =110000(2)

题解

弟弟地真实…
先把边的异或转到点上,每个点的权设为与它相连的边的异或和
然后就是一个带修改的线性基问题了
这类问题都可以用一种思想:线性基的删除只有在没有人能替代它的位置时删除
考虑线段树分治
单层修改回去的时候,显然上层不会有数能够让我这个被修改过的位置出现数,所以可以直接叉掉
然后就没了…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=505;
const int MAXM=1005;
const int MAXL=1005;
bitset<MAXL> ad;
bitset<MAXL> pt[MAXN];int lst[MAXN];
bitset<MAXL> bit[MAXM*4][MAXM];
bitset<MAXL> lin[MAXL];int ok[MAXL];
bitset<MAXL> ans;
int cnt[MAXM*4];

int sta1[MAXM],sta2[MAXM],tp1,tp2;

int push()
{
	for(int i=1000;i>=0;i--)if(ad[i])
	{
		if(!ok[i]){ok[i]=1,lin[i]=ad;return i;}
		else ad=ad^lin[i];
	}
	return -1;
}
void in(int now)
{
	for(int i=1;i<=cnt[now];i++)
	{
		ad=bit[now][i];int temp=push();
		if(temp!=-1)sta2[++tp2]=temp;
	}
	sta1[++tp1]=tp2;
}
void ot()
{
	int ls=sta1[--tp1];
	while(tp2!=ls)ok[sta2[tp2--]]=0;
}
bool check()
{
	for(int i=1000;i>=0;i--)
	{
		if(ad[i]==1&&ans[i]!=1)return true;
		if(ad[i]!=1&&ans[i]==1)return false;
	}
	return false;
}
void solve()
{
	ans.reset();
	for(int i=1000;i>=0;i--)if(ok[i])
	{
		ad=ans^lin[i];
		if(check())ans=ad;
	}
	bool ok1=false;
	for(int i=1000;i>=0;i--)
	{
		if(!ans[i]&&ok1)pr1(0);
		else if(ans[i])pr1(1),ok1=true;
	}
	if(!ok1)pr1(0);
	puts("");
}
void insert(int now,int l,int r,int ql,int qr)
{
	if(l==ql&&r==qr){bit[now][++cnt[now]]=ad;return ;}
	int mid=(l+r)/2,lc=now<<1,rc=now<<1|1;
	if(qr<=mid)insert(lc,l,mid,ql,qr);
	else if(mid+1<=ql)insert(rc,mid+1,r,ql,qr);
	else insert(lc,l,mid,ql,mid),insert(rc,mid+1,r,mid+1,qr);
}
void qry(int now,int l,int r)
{
	in(now);
	if(l==r){solve();ot();return ;}
	int mid=(l+r)/2,lc=now<<1,rc=now<<1|1;
	qry(lc,l,mid);
	qry(rc,mid+1,r);
	ot();
}
int n,m;
char ch[11000];
int main()
{
	int T=read();
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();scanf("%s",ch);int len=strlen(ch);
		if(x==y)continue;
		ans.reset();
		for(int j=len-1;j>=0;j--)ans[len-j-1]=ch[j]-'0';
		if(!lst[x]){pt[x]=ans;lst[x]=i;}
		else
		{
			ad=pt[x];
			insert(1,1,m,lst[x],i-1);
			pt[x]^=ans;lst[x]=i;
		}
		if(!lst[y]){pt[y]=ans;lst[y]=i;}
		else
		{
			ad=pt[y];
			insert(1,1,m,lst[y],i-1);
			pt[y]^=ans;lst[y]=i;
		}
	}
	for(int i=1;i<=n;i++)if(lst[i])
	{
		ad=pt[i];
		insert(1,1,m,lst[i],m);
	}
	qry(1,1,m);
//	while(1);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值