• loj#2013. 「SCOI2016」幸运数字 点分治/线性基


    题目链接

    loj#2013. 「SCOI2016」幸运数字

    题解

    和树上路径有管...点分治吧
    把询问挂到点上
    求出重心后,求出重心到每个点路径上的数的线性基
    对于重心为lca的合并寻味,否则标记下传
    对于每个询问,只需要暴力合并两个线性基即可
    每个点只会被加到logn个线性基里,所以总复杂度为O(nlogn60 + q60*2)
    然后我写了句memset(b,0,sizeof 0)...被卡了1h...

    代码

    #include<cstdio> 
    #include<vector> 
    #include<cstring> 
    #include<algorithm> 
    
    inline int read() { 
    	int x = 0,f = 1; 
    	char c = getchar(); 
    	while(c < '0' || c > '9') c = getchar(); 
    	while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = getchar(); 
    	return x * f; 
    } 
    
    inline long long Read() { 
    	long long x = 0,f = 1; 
    	char c = getchar(); 
    	while(c < '0' || c > '9') c = getchar(); 
    	while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = getchar(); 
    	return x * f; 
    } 
    #define LL long long 
    const int maxn = 200007; 
    int n,m; 
    long long val[maxn]; 
    struct node {
    	int v,next; 
    } edge[maxn << 1]; 
    int head[maxn],num = 0; 
    inline void add_edge(int u,int v) { 
    	edge[++ num].v = v;edge[num].next = head[u],head[u] = num; 
    } 
    
    struct Base { 
    	LL b[63]; 
    	inline void clear() {memset(b,0,sizeof b); } 
    	inline void insert(LL x) { 
    		for(int i = 60;~i;-- i) 
    			if(x >> i & 1)  
    				if(b[i]) x ^= b[i]; 
    				else {b[i] = x; break;}  
    	} 
    	inline void merge(const Base &x) { 
    		for(int i = 60;~i;-- i) 
    			if(x.b[i]) insert(x.b[i]); 
    	} 
    	inline LL query() {	
    		LL ret = 0; 
    		for(int i = 60;~i;-- i) 
    			ret = std::max(ret ^ b[i],ret); 
    		return ret; 
    	} 
    } base[maxn]; 
    
    int U[maxn],V[maxn],sz[maxn],bel[maxn]; 
    LL ans[maxn]; 
    std::vector<int>q[maxn]; 
    bool vis[maxn]; 
    
    int root = 0,mt; 
    void get_root(int x,int fa,int tot) {
    	sz[x] = 1; int mx = 0; 
    	for(int i = head[x];i;i = edge[i].next) { 
    		int v = edge[i].v; 
    		if(v == fa || vis[v]) continue; 
    		get_root(v,x,tot); 
    		sz[x] += sz[v]; 
    		mx = std::max(mx,sz[v]); 
    	} 
    	mx = std::max(tot - sz[x],mx); 
    	if(mx < mt) root = x, mt = mx; 
    } 
    void dfs(int x,int fa,int Bel) {
    	bel[x] = Bel; base[x] = base[fa]; base[x].insert(val[x]); 
    	for(int i = head[x];i;i = edge[i].next) 
    		if(edge[i].v != fa && !vis[edge[i].v]) 
    			dfs(edge[i].v,x,Bel);   
    } 
    int tq[maxn]; 
    void solve(int x) { 
    	if(!q[x].size()) return; 
    	mt = 20005; get_root(x,x,sz[x]); 
    	vis[root] = 1;
    	bel[root] = root; 
    	base[root].clear(); 
    	base[root].insert(val[root]); 
    	for(int i = head[root];i;i = edge[i].next) 
    		if(!vis[edge[i].v]) 
    		dfs(edge[i].v,root,edge[i].v); 
    	int tot = q[x].size(); 
    	for(int i = 0;i <= tot;++ i) tq[i] = q[x][i]; 
    	q[x].clear(); 
    	Base tmp; 
    	for(int i = 0,id;i < tot;++ i) { 
    		if(bel[U[id = tq[i]]] == bel[V[id]]) 
    			q[bel[U[id]]].push_back(id); 
    		else  
    			tmp = base[U[id]],
    			tmp.merge(base[V[id]]),
    			ans[id] = tmp.query();  
    	} 
    	for(int i = head[root];i;i = edge[i].next) 
    		if(!vis[edge[i].v]) solve(edge[i].v);   
    } 
    int main() { 
    	//freopen("lucky1.in","r",stdin); 
    	n = read();m = read();  
    	for(int i = 1;i <= n;++ i) val[i] = Read(); 
    	for(int u,v,i = 1;i < n;++ i) { 
    		u = read(),v = read(); 
    		add_edge(u,v); add_edge(v,u); 
    	} 
    	for(int i = 1;i <= m;++ i) {
    		U[i] = read(),V[i] = read(); 
    		if(U[i] == V[i]) ans[i] = val[U[i]]; 
    		else q[1].push_back(i); 
    	} 
    	sz[1] = n; solve(1); 
    	for(int i = 1;i <= m;++ i) 
    	printf("%lld
    ",ans[i]); 
    	return 0; 	
    } 
    
  • 相关阅读:
    拖拽更改窗口大小
    一个窗口移动时,另一个窗口跟随移动
    xcode使用技巧
    同一个解决方案中,多个项目间相互引用,无法打开源文件
    截图时窗口自动识别
    C++使用sqlite时,中文字符显示乱码问题
    sqlite3配置与使用
    duilib控件与属性说明
    xml文件编写
    线程及安全相关
  • 原文地址:https://www.cnblogs.com/sssy/p/9574850.html
Copyright © 2020-2023  润新知