[HAOI2017]八纵八横 线性基

题面

题面

题解

观察到题目中的 “内陆经济环” 不好处理,因此我们把它拆成 “内陆经济链”。
对于1号节点,我们创建一个它的复制节点n + 1号节点,这个节点继承1号节点的所有边,可以发现,一个1到1的内陆经济环,和一个1到n + 1的内陆经济链是等价的,因此我们只需要考虑如何在一个变化的图上维护一个点到另一个点的最大xor和即可。
观察到删边只会删去后来加入的边,所以就很好处理了,我们用线段树分治(时间分治)来维护。
具体求从1到n + 1的最大xor和的方法参见此题(是一个常见套路,就不在赘述了):[WC2011]最大XOR和路径 线性基
于是我们的目的仅仅是维护当前图中所有简单环构成的线性基。
因为线段树分治最深也就log,所以同时维护log个线性基的时间空间都还是可承受的。
因此我们每到一个新节点就建一个新的线性基,然后加入新增的环,退出时再删去即可。
考虑如何快速求新增的环。
因为在dfs树上,一条非树边代表一个简单环,所以一个新增的,连接x ---> y 的边最多带来一个新环,而这个新环,肯定是经过这条新边,从x走到y,再走回x的路径所构成的一个环。
因为xor的特性,所以我们可以用x到rot的路径 + y到rot的路径 + 新边来异或出这个环的异或值。
因此我们只需要知道一个点新增了哪些边就可以了。

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 550
#define ac 2100
#define maxn 8000
#define maxm 2010000 

int n, m, all, cnt;
int dead[ac], start[ac], id[ac];
int Head[AC], date[ac], Next[ac], tot;
bitset<1100> len[ac], ans[ac], val[AC], link[maxn];
struct road{int x, y, id;bitset<1100> v;}way[ac];//注意一条边会且只会带来一个环(因为原图联通)
bool vis[AC];
char ss[ac];

//int s[AC], top;//存下待分配的线性基编号
struct basis{//第几层就用第几个线性基
    bitset<1100> f[1100];//第1000位为最高位
    void clear() {for(R i = 1; i <= 1000; i += 2) f[i] = f[i + 1] = 0;}

    void ins(bitset<1100> x)
    {
        for(R i = 1000; i; i --)
        {
            if(!x[i]) continue;
            if(f[i] == 0) {f[i] = x; break;}
            else x ^= f[i];
        }
    }

}f[50];//最多同时用到log个线性基

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

/*inline void add_new(int f, int w)//f为线段树上的一个点,w是一条边的编号
{date_[++ tot_] = w, Next_[tot_] = Head_[f], Head_[f] = tot_;}*/

inline void add(int f, int w, bitset<1100> S)
{
    date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S;
    date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot, len[tot] = S;
}

void dfs(int x, bitset<1100> have)//找最初的环,
{
    vis[x] = true, val[x] = have;
    for(R i = Head[x]; i; i = Next[i])
    {
        int now = date[i];
        if(vis[now]) f[0].ins(have ^ len[i] ^ val[now]);
        else dfs(now, have ^ len[i]);
    }
}

void change(int x, int l, int r, int ll, int rr, int w)//给[ll, rr]加入一条边,其编号为w
{
    if(l == ll && r == rr){link[x][w] = true; return ;}
    int mid = (l + r) >> 1;
    if(rr <= mid) change(x << 1, l, mid, ll, rr, w);
    else if(ll > mid) change((x << 1) + 1, mid + 1, r, ll, rr, w);
    else
    {
        change(x << 1, l, mid, ll, mid, w);
        change((x << 1) + 1, mid + 1, r, mid + 1, rr, w);
    }
}

void solve(int x, int l, int r, int dep)
{
    f[dep] = f[dep - 1];
    for(R i = 1; i <= all; i ++)
        if(link[x][i]) f[dep].ins(val[way[i].x] ^ val[way[i].y] ^ way[i].v);
    if(l == r)
    {
        ans[l] = val[n + 1];
        for(R i = 1000; i; i --)
            if(ans[l][i] == 0) ans[l] ^= f[dep].f[i];
        return ;
    }
    int mid = (l + r) >> 1;
    solve(x << 1, l, mid, dep + 1);
    solve((x << 1) + 1, mid + 1, r, dep + 1);
}

bitset<1100> get()
{
    bitset<1100> tmp;
    scanf("%s", ss + 1); int t = strlen(ss + 1);
    for(R j = t, l = 1; j; j --, l ++) tmp[l] = (ss[j] == '1'); //存下边权
    return tmp;
}

void pre()
{
    n = read(), m = read(), all = read();
    bitset<1100> tmp;
    for(R i = 1; i <= m; i ++)
    {
        int x = read(), y = read();
        if(x > y) swap(x, y);
        tmp = get();
        add(x, y, tmp);
        if(x == 1) add(n + 1, y, tmp);
    }
}

void build()
{
    for(R i = 1; i <= all; i ++)
    {
        scanf("%s", ss + 1);
        if(ss[1] == 'A') //加边
        {
            id[++ cnt] = i;//id[x]存下边x当前是谁在承受修改
            start[i] = i, dead[i] = all;
            way[i].x = read(), way[i].y = read(), way[i].v = get();
        }
        else if(ss[2] == 'a') {dead[id[read()]] = i - 1;}//删边
        else //修改边权,看做删去一条旧边生成一条新边,并且新边承担后面旧边的修改
        {
            int x = read();//承担旧边的修改,并删除旧边
            way[i] = way[id[x]], way[i].v = get();
            start[i] = i, dead[i] = all, dead[id[x]] = i - 1, id[x] = i;//除了初始化id[x]外,其他的地方都要用到id[x]
        }//所以id[x] = i放在最后面修改,在前面的x都要用id[x]代替
    }
    for(R i = 1; i <= all; i ++)
        if(start[i]) change(1, 1, all, start[i], dead[i], i);
}

void work()
{
    for(R i = 1000; i; i --)
        if(!ans[0][i]) ans[0] ^= f[0].f[i];
    for(R i = 0; i <= all; i ++)
    {
        int l = 1000;
        while(!ans[i][l] && l) -- l;
        for(R j = l; j; j --) printf("%c", (ans[i][j] == 1) ? '1' : '0');
        if(!l) printf("0");
        printf("\n");
    }
}

int main()
{
//  freopen("in.in", "r", stdin);
    pre();
    dfs(1, ans[0]);//反正此时ans[0]是空的
    build();
    if(all) solve(1, 1, all, 1);
    work();
//  fclose(stdin);
    return 0;
}

原文地址:https://www.cnblogs.com/ww3113306/p/10354362.html

时间: 2024-11-08 02:01:23

[HAOI2017]八纵八横 线性基的相关文章

loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)

题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se second #define pb push_back #define bit bitset<B + 1> using namespace std; const int MAXN = 501, B = 1001, SS = 4001; inline int read() { char c = getchar

【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路的两端都是城市(可能两端是同一个城市),保证任意两个城市都可以通过高速公路互达. 国正在筹划“八纵八横”的高铁建设计划,计划要修建一些高速铁路,每条高速铁路两端也都是城市(可能两端是同一个城市),也都有一个非负整数的经济影响因子.国家还计划在“八纵八横”计划建成之后,将“一带一路”扩展为“一带_路一

HAOI2017 八纵八横——线段树分治+线性基

题目大意 给定一个图,每次加一些边,或者删掉一些后来加上去的边,定义一个环的价值为环上所有的边的异或和,重复走的边重复算.每次询问这个时刻图中的所有经过1号点的环的最大价值. 思路 首先考虑对于一个静态的图如何求解图中所有经过1号点的环的最大价值,发现这个经过1号点就是唬人的,图中任意一个环都可以经过1号点再走回来. 于是题目变成了求解图中环的最大价值,可以将图中所有的简单环给拎出来放到线性基里面求最大价值,不难发现这是对的. 然后题目转化为了如何求图中所有的简单环,一般我们可以直接对图dfs找

Xor HYSBZ - 2115 (线性基)

Xor HYSBZ - 2115 题意:给一个树,求1到n的最长路径.这里的路径定义为异或和. 线性基~~ 1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 struct LiBase{ 5 ll a[63]; 6 //初始化 7 void init(){ 8 memset(a,0,sizeof(a)); 9 } 10 //插入 11 bool insert_(ll x){ 12 for(int

线性基小节

1.线性基的异或集合中每个元素的异或方案唯一. 2.线性基二进制最高位互不相同. 3.线性基中元素互相异或,异或集合不变. 摘自百度文库 线性基能相互异或得到原集合的所有相互异或得到的值. 线性基是满足性质1的最小的集合 线性基没有异或和为0的子集. 证明: 反证法:设线性基S={a1,a2...,an}: 若有子集a1^a2^...^at=0,则a1=a2^a3^...^at,则舍弃a1后一定能通过剩余的元素异或出所有需要a1参与异或的值.设Y=a1^X,因为{a1,a2,...,an}是一组

[BeiJing2011]元素[贪心+线性基]

2460: [BeiJing2011]元素 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1245  Solved: 652[Submit][Status][Discuss] Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术.那时人们就认识到,一个法杖的法力取决于使用的矿石.一般地,矿石越多则法力越强,但物极必反:有时,人们为了获取更强的法力而使用了很多矿石,却在炼制过程中

BZOJ 3105: [cqoi2013]新Nim游戏 [高斯消元XOR 线性基]

以后我也要用传送门! 题意:一些数,选择一个权值最大的异或和不为0的集合 终于有点明白线性基是什么了...等会再整理 求一个权值最大的线性无关子集 线性无关子集满足拟阵的性质,贪心选择权值最大的,用高斯消元判断是否和已选择的线性相关 每一位记录pivot[i]为i用到的行 枚举要加入的数字的每一个二进制为1的位,如果有pivot[i]那么就异或一下(消元),否则pivot[i]=这个数并退出 如果最后异或成0了就说明线性相关... #include <iostream> #include &l

【BZOJ2844】albus就是要第一个出场 线性基 高斯消元

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43456773"); } 题意:需要注意的是空集(0)是天生被包括的,我为了这个WA了好久~拍了好久,醉了好久~ 题解: 首先有一个我并不知道是为什么(甚至不知道它对不对)的性质: 每一种权值会出现2的自由元(n-线性基个数)次方 次. 感性

[bzoj 2460]线性基+贪心

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2460 网上很多题目都没说这个题目的证明,只说了贪心策略,我比较愚钝,在大神眼里的显然的策略还是想证明一下才安心--所以这里记录一下证明过程. 贪心策略:按魔力值从大到小排序,从大往小往线性基里插,如果成功插入新元素,就选这个,如果插不进去,就不选这个. 证明: 设有n个材料,每个材料的属性值是x[1],x[2],...,x[n],魔力值是v[1],v[2],...,v[n],这里假设v已