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

题目大意

给定一个图,每次加一些边,或者删掉一些后来加上去的边,定义一个环的价值为环上所有的边的异或和,重复走的边重复算。每次询问这个时刻图中的所有经过1号点的环的最大价值。

思路

首先考虑对于一个静态的图如何求解图中所有经过1号点的环的最大价值,发现这个经过1号点就是唬人的,图中任意一个环都可以经过1号点再走回来。

于是题目变成了求解图中环的最大价值,可以将图中所有的简单环给拎出来放到线性基里面求最大价值,不难发现这是对的。

然后题目转化为了如何求图中所有的简单环,一般我们可以直接对图dfs找环,这个题目可以随便建出一颗生成树,任何一条非树边都对应了一个简单环,于是我们每次只需要在树上加边删边即可。

但是线性基不支持删除啊,线段树分治就好了。

/*=======================================
 * Author : ylsoi
 * Time : 2019.4.3
 * Problem : luogu3733
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define pii pair<bitset<maxn>,int>
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu3733.in","r",stdin);
    freopen("luogu3733.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

string proc(){
    ifstream f("/proc/self/status");
    return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=1000+10;
int n,m,q,tot;
struct edge{
    int u,v,l,r;
    bitset<maxn>w;
}e[maxn];
vector<pii>G[maxn];
vector<edge>lis;
bitset<maxn>dis[maxn];

void read_bitset(bitset<maxn>&x){
    static char strbuf[maxn];
    char ch=getchar(); int tp=0;
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);ch=getchar())strbuf[tp++]=ch;
    tot=max(tot,tp),x.reset();
    DREP(i,tp-1,0)if(strbuf[tp-1-i]=='1')x.set(i);
}

void output(bitset<maxn>&x,char ch='\n'){
    int p=0;
    DREP(i,tot-1,0)if(x[i]){p=i;break;}
    DREP(i,p,0)putchar(x[i]+48);
    putchar(ch);
}

int fa[maxn];
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}

void dfs_init(int u,int fh){
    REP(i,0,G[u].size()-1){
        bitset<maxn>w=G[u][i].fi;
        int v=G[u][i].se;
        if(v==fh)continue;
        /*cout<<u<<" "<<v<<endl;
        output(w);*/
        dis[v]=dis[u]^w;
        dfs_init(v,u);
    }
}

struct liner_base{
    bitset<maxn>b[maxn];
    liner_base(){DREP(i,tot-1,0)b[i].reset();}
    void insert(bitset<maxn>x){
        DREP(i,tot-1,0)if(x[i]){
            if(!b[i][i]){b[i]=x;break;}
            x^=b[i];
        }
    }
    bitset<maxn>query(){
        bitset<maxn>ret(0);
        DREP(i,tot-1,0)if(!ret[i] && b[i][i])
            ret^=b[i];
        return ret;
    }
}T[21];

void init(){
    read(n),read(m),read(q);
    REP(i,1,n)fa[i]=i;

    int u,v;
    REP(i,1,m){
        read(u),read(v),read_bitset(e[i].w);
        e[i].u=u,e[i].v=v;
        //output(e[i].w);
        if(find(u)!=find(v)){
            fa[find(u)]=find(v);
            G[u].push_back(mk(e[i].w,v));
            G[v].push_back(mk(e[i].w,u));
        }
        else lis.push_back(e[i]);
    }

    dfs_init(n,0);
}

#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc (o<<1|1)
#define lson lc,l,mid
#define rson rc,mid+1,r

vector<bitset<maxn> >t[maxn<<2];
void insert(int o,int l,int r,int L,int R,bitset<maxn>&x){
    if(L<=l && r<=R)t[o].push_back(x);
    else{
        if(L<=mid)insert(lson,L,R,x);
        if(R>=mid+1)insert(rson,L,R,x);
    }
}

void divide(int o,int l,int r,int dep){
    REP(i,0,t[o].size()-1)
        T[dep].insert(t[o][i]);
    if(l==r){
        bitset<maxn>ans=T[dep].query();
        output(ans);
    }
    else{
        T[dep+1]=T[dep];
        divide(lson,dep+1);
        T[dep+1]=T[dep];
        divide(rson,dep+1);
    }
}

vector<edge>tl[maxn];
void work(){
    char opt[11];
    int u,v,cnt=0;
    bitset<maxn>w;
    REP(i,0,lis.size()-1){
        w=dis[lis[i].u]^dis[lis[i].v]^lis[i].w;
        insert(1,1,q+1,1,q+1,w);
    }
    REP(i,1,q){
        scanf("%s",opt);
        if(opt[0]=='A'){
            read(u),read(v),read_bitset(w);
            tl[++cnt].push_back((edge){u,v,i+1,q+1,w});
        }
        else if(opt[1]=='h'){
            read(u),read_bitset(w);
            int las=tl[u].size()-1;
            tl[u][las].r=i;
            tl[u].push_back((edge){tl[u][las].u,tl[u][las].v,i+1,q+1,w});
        }
        else{
            read(u);
            int las=tl[u].size()-1;
            tl[u][las].r=i;
        }
    }
    REP(i,1,cnt)REP(j,0,tl[i].size()-1){
        w=dis[tl[i][j].u]^dis[tl[i][j].v]^tl[i][j].w;
        insert(1,1,q+1,tl[i][j].l,tl[i][j].r,w);
    }
    divide(1,1,q+1,0);
}

int main(){
    File();

    init();

    work();

    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10647955.html

时间: 2024-10-09 16:52:38

HAOI2017 八纵八横——线段树分治+线性基的相关文章

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

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

Codeforces 938G 线段树分治 线性基 可撤销并查集

Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问x到y的路径异或最小值 保证图在任意时刻连通 首先连通图路径异或相当于从x到y的任意一条路径再异或上若干个环得到的,只要在dfs过程中把非树边成的环丢到线性基里就好了,其他环一定可以通过这些环异或组合出来 有加边删边操作怎么做呢?线段树时间分治!注意到不能保证在线段树的任意一个节点图是连通的,需要用

2017 ICPC西安区域赛 A - XOR (线段树并线性基)

链接:https://nanti.jisuanke.com/t/A1607 题面: Consider an array AA with n elements . Each of its element is A[i]A[i] (1 \le i \le n)(1≤i≤n) . Then gives two integers QQ, KK, and QQ queries follow . Each query , give you LL, RR, you can get ZZ by the foll

线段树维护线性基并——17西安icpc a

#include<bits/stdc++.h> using namespace std; #define N 10005 int a[N],n,k,q; struct LB{ int b[35]; LB(){memset(b,0,sizeof b);} int check(int x){ for(int i=29;i>=0;i--)if(x>>i & 1){ if(!b[i])return 0; x^=b[i]; } return 1; } void insert(i

UVALive - 8512 线段树维护线性基(仅观赏)

题意:给定\(a[1...n]\),\(Q\)次询问求\(A[L...R]\)的异或组合再或上\(K\)的最大值 目前提交处于TLE状态,原因待查 #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> #include<ve

bzoj 4184: shallot (线段树维护线性基)

题面 \(solution:\) \(code:\) #include<iostream> #include<cstdio> #include<iomanip> #include<algorithm> #include<cstring> #include<cstdlib> #include<ctime> #include<cmath> #include<vector> #include<que

线段树分治总结

目录 类型一 例题1:八纵八横 代码: 例题2:时空旅行 首先,要求可以离线. 线段树分治有两种. 类型一 操作基于区间,单点询问. 有时,进行的一种操作可以快速完成,但是,要实现这种操作的逆操作较难. 因为,通常情况下,需要实现的逆操作都是很久以前执行的. 但是,如果只撤销上次操作,就会简单得多. 比如,维护一些连通性,或直径,线性基等问题. 这类问题加边很好做,但删边很难实现. 我们可以扫一遍操作,得到每个操作的有效区间. 然后,将每个添加操作的有效区间按在线段树上,然后遍历这颗线段树同时处

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

线段树分治

2014徐寅展论文<线段树在一类分治问题上的应用>读后感. 线段树分治 线段树分治其实就是有撤销操作的时间分治. 题目让你维护一些信息,每次可以询问,可以执行一种操作,也可以将之前的某个这种操作撤回. 操作容易维护,但撤回操作不容易维护. 需要将操作,询问都离线下来.将时间轴画出来,那么每个操作只在时间轴上的一个区间内生效. 用线段树给这个区间打上这个操作的标记,维护信息. TJOI2018 数学计算 小豆现在有一个数x,初始值为1. 小豆有Q次操作,操作有两种类型: m: x = x * m