BZOJ 4644: 经典傻逼题 线段树分治 线性基

4644: 经典傻逼题

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 135  Solved: 66
[Submit][Status][Discuss]

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
前三条边加入之后的答案较为显然,考虑后三条边,加入第六条边之前, 考
虑点集{1,2},它对应的割只有第四条边, 因此答案就是第四条边的权值,考虑加
入最后一条边以后的情况,此时点集{1,2}对应的割变成了第四条边和第六条边组
成的集合,权值也发生了相应的改变。 点集{2}对应的割是第五条边和第六条边
组成的集合, 可以证明这就是权值最大的割,权值为1011(2) ⊕ 111011(2) =110000(2)

线段树分治 线性基

只要前置技能足够,思路出来的还是很快的

括弧:由于我太懒,题解就没有了。。。

线段树中插点权

维护一个巨大的线性基 最开始看代码没看懂GG


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=1010;

int n,m;

bitset<N>b,V[N],bas[N];

char s[N];

int last[N];

struct seg_tree{vector<bitset<N> >b;vector<int>p;}tr[N<<2];

void modify(int k,int l,int r,int x,int y,bitset<N> &val)
{
	if(l>=x&&r<=y){tr[k].b.push_back(val);return ;}
	int mid=(l+r)>>1;
	if(x<=mid)modify(k<<1,l,mid,x,y,val);
	if(y>mid)modify(k<<1|1,mid+1,r,x,y,val);
}

void solve(int k,int l,int r)
{
	register int i,j;
	for(i=0;i<tr[k].b.size();++i)
	for(j=1;j<N;++j)if(tr[k].b[i][j])
	{
		if(!bas[j][j]){bas[j]=tr[k].b[i];tr[k].p.push_back(j);break;}
		else tr[k].b[i]^=bas[j];
	}
	if(l==r)
	{
		b.reset();
		for(i=1;i<N;++i)if(bas[i][i]&&!b[i])b^=bas[i];
		for(i=1;i<N;++i)if(b[i])break;if(i>=N)putchar('0');
		for(;i<N;++i)putchar(b[i]+'0');puts("");
	}
	else
	{
		int mid=(l+r)>>1;
		solve(k<<1,l,mid);solve(k<<1|1,mid+1,r);
	}
	for(i=0;i<tr[k].p.size();++i)
	bas[tr[k].p[i]].reset();
}

int main()
{
	read();n=read();m=read();
	register int i,j,u,v,k;
	for(i=1;i<=m;++i)
	{
		u=read();v=read();scanf("%s",s);if(u==v)continue;
		b.reset();k=strlen(s);
		for(j=0;j<k;++j)b[N-k+j]=s[j]-'0';
		if(last[u])modify(1,1,m,last[u],i-1,V[u]);
		if(last[v])modify(1,1,m,last[v],i-1,V[v]);
		last[u]=last[v]=i;
		V[u]^=b;V[v]^=b;
	}
	for(i=1;i<=n;++i)if(last[i])modify(1,1,m,last[i],m,V[i]);
	solve(1,1,m);
	return 0;
}
/*
0
3 6
1 2 1
1 2 1
3 3 111
1 3 101101
1 2 1011
2 3 111011

1 0 0
101101
101101
110000
*/

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值