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

经典傻逼题

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)


如果它是经典题,那么咱就是shabi……
因为这么经典的操作居然现在才第一次写……


思路:
这个叫线段树分治的东西,好像在哪里见过……
而且好像当时被叫做时间线段树……

首先考虑异或的性质。
可以发现,如果一条边的两个点选中,那么这条边将没有贡献。
于是如果咱们把边权异或到这条边的两个点上,可以恰好使用选中的节点的点权异或和来描述所有满足条件的边的贡献。
因为一旦一条边的两个点均被选中,那么这个点的边权就被异或运算消掉了。

那么现在要求从所有点中选出一个子集使得点权异或和最大。
于是考虑线性基贪心,从高位向低位枚举贪心,若答案异或上当前元素可以变大,那就异或当前元素,即可得到答案。

然而线性基很难兹瓷修改。
可以考虑针对高斯消元把原先的某一个点权的影响消去再重新插入。
然而貌似很难写。

于是使用线段树分治,用一棵线段树描述一个时间轴。
对于每个点,它在整个操作过程中的权值变化可以按时间分成一些内部权值相同的小段。
对于每一段这样的小段,在线段树上对应时间区间打上当前权值的标记,代表在这一段的时间内这个点的权值是这一段的值。

最后dfs一遍整棵线段树,每遇到一层节点就向线性基插入当前层的所有tag中的点权,同时每个节点的线性基均需继承父亲节点的信息。
这样递归到叶子结点时便可获得叶子结点所代表的时间点处的线性基,此时可直接输出答案。

于是就做完了~

#include<bits/stdc++.h>

using namespace std;

const int N=509;
const int M=1009;
const int LEN=1009;
const int K=1000;
typedef bitset<LEN> bit;
#define mid ((l+r)>>1)

inline void read(bit &b)
{
    static char s[LEN];
    scanf("%s",s);
    int len=strlen(s);
    reverse(s,s+len);
    b.reset();
    for(int i=0;i<len;i++)
        b[i]=s[i]-'0';
}

inline void print(const bit &b)
{
    int j;
    for(j=K;~j;j--)
        if(b[j])
            break;
    if(j<0)putchar('0');
    for(int i=j;i>=0;i--)
        putchar(b[i]?'1':'0');
    putchar('\n');
}

struct basis
{
    bit a[LEN];
    basis()
    {
        for(int i=K;i>=0;i--)
            a[i].reset();
    }
    inline void add(bit x)
    {
        for(int i=K;i>=0;i--)
            if(x[i])
                x^=a[i];
        for(int i=K;i>=0;i--)
            if(x[i] && !a[i].any())
            {
                a[i]=x;
                return;
            }
    }
    inline void output()
    {
        bit ret;
        for(int i=K;~i;i--)
            if(a[i].any() && !ret[i])
                ret^=a[i];
        print(ret);
    }
};

int n,m,lst[N];
bit lbit[N],tmp;
vector<bit> t[M<<2];

inline void add(int x,int l,int r,int dl,int dr,const bit &b)
{
    if(dl==l && r==dr)
    {
        t[x].push_back(b);
        return;
    }
    if(dr<=mid)
        add(x<<1,l,mid,dl,dr,b);
    else if(mid<dl)
        add(x<<1|1,mid+1,r,dl,dr,b);
    else
    {
        add(x<<1,l,mid,dl,mid,b);
        add(x<<1|1,mid+1,r,mid+1,dr,b);
    }
}

inline void dfs(int x,int l,int r,basis b)
{
    for(int i=0,e=t[x].size();i<e;i++)
        b.add(t[x][i]);
    if(l<r)
        dfs(x<<1,l,mid,b),dfs(x<<1|1,mid+1,r,b);
    else
        b.output();
}

int main()
{
    scanf("%*d%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        read(tmp);
        if(u==v)continue;
        if(lst[u])
            add(1,1,m,lst[u],i-1,lbit[u]);
        lst[u]=i;lbit[u]^=tmp;
        if(lst[v])
            add(1,1,m,lst[v],i-1,lbit[v]);
        lst[v]=i;lbit[v]^=tmp;
    }

    for(int i=1;i<=n;i++)
        if(lst[i]<=m && lst[i])
            add(1,1,m,lst[i],m,lbit[i]);

    dfs(1,1,m,basis());
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值