zoukankan      html  css  js  c++  java
  • SPOJ1825:Free Tour II

    浅谈树分治:https://www.cnblogs.com/AKMer/p/10014803.html

    题目传送门:https://www.spoj.com/problems/FTOUR2/

    我们设(f_i)表示只经过(i)个黑点的路径的最长距离。从目前的根到当前点的路径上黑点个数为(cnt),路径长度为(dis),那么我就可以用(dis+max){(f_i|i+cntleqslant k)}来更新答案。我们依次“询问当前的根的子树去更新答案,然后遍历这棵子树去更新(f)”,这样可以保证更新答案的两条路径肯定不在同一个子树内。

    对于边分,我们只需要遍历一个块更新(f)数组,然后前缀最大值一下,遍历另外一个块去询问即可。

    但是对于点分,这样显然不行,因为一个点删掉之后可能会有很多个块,每个块每次更新完之后都前缀最大值一次可以到(n^2)的复杂度,显然直接爆炸了。

    因为我们要支持动态更改(f)数组,还要支持动态访问前缀最大值,所以我们可以用树状数组来维护(f)数组,这样子复杂度是(O(nlog^2n))的,因为多次遍历和树状数组的清空,常数很大,亲测(TLE)

    我们令(dep_i)表示(i)号子树内黑点个数最多的链(起点必须是当前的根)上有多少个黑点,然后把子树按照(dep)排序,从小到大依次去执行上述操作,这样子是(O(n))的。前缀最大值的更新复杂度为(sumlimits_{vin son[u]}dep_v)。因为黑点个数肯定小于总点数,所以这样子更新前缀最大值也是(O(n))的。至于按照(dep)去排序儿子,每个儿子顶多会被排序(degree)次,总数不会超过边数,也是(nlogn)的,所以复杂度就被降为(O(nlogn))了。

    时间复杂度:(O(nlogn))

    空间复杂度:(O(n))

    点分治版代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int maxn=2e5+5,inf=2e9+10;
    
    int siz[maxn],f[maxn];
    bool black[maxn],vis[maxn];
    int n,k,m,tot,ans=0,N,rt,mx;
    int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    struct SON {
    	int node,dep,val;
    
    	SON() {}
    
    	SON(int _node,int _dep,int _val) {
    		node=_node,dep=_dep,val=_val;
    	}
    
    	bool operator<(const SON &a)const {
    		return dep<a.dep;
    	}
    }to[maxn];
    
    void add(int a,int b,int c) {
    	pre[++tot]=now[a];
    	now[a]=tot,son[tot]=b,val[tot]=c;
    }
    
    void find_rt(int fa,int u) {
    	int res=0;siz[u]=1;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v]&&v!=fa)find_rt(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
    	res=max(res,N-siz[u]);
    	if(res<mx)mx=res,rt=u;
    }
    
    int dfs(int fa,int u,int cnt) {
    	int res=cnt;siz[u]=1;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v]&&v!=fa)res=max(res,dfs(u,v,cnt+black[v])),siz[u]+=siz[v];
    	return res;
    }
    
    void query(int fa,int u,int dis,int cnt) {
    	if(cnt>k)return;
    	ans=max(ans,dis+f[min(mx,k-cnt)]);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v]&&v!=fa)query(u,v,dis+val[p],cnt+black[v]);
    }
    
    void solve(int fa,int u,int dis,int cnt) {
    	if(cnt>k)return;
    	f[cnt]=max(f[cnt],dis);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v]&&v!=fa)solve(u,v,dis+val[p],cnt+black[v]);
    }
    
    void work(int u,int size) {
    	N=size,mx=rt=n+1,find_rt(0,u);
    	u=rt,vis[u]=1,tot=0;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v])to[++tot]=SON(v,dfs(u,v,black[u]+black[v]),val[p]);
    	sort(to+1,to+tot+1);mx=to[tot].dep;
    	for(int i=0;i<=mx;i++)f[i]=-inf;
    	for(int i=1;i<=tot;i++) {
    		query(u,to[i].node,to[i].val,black[to[i].node]);
    		solve(u,to[i].node,to[i].val,black[u]+black[to[i].node]);
    		mx=to[i].dep;mx=min(mx,k);
    		for(int j=1;j<=mx;j++)
    			f[j]=max(f[j],f[j-1]);
    	}
    	for(int i=0;i<=mx;i++)ans=max(ans,f[i]);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v])work(v,siz[v]);
    }
    
    int main() {
    	n=read(),k=read(),m=read();
    	for(int i=1;i<=m;i++) {
    		int u=read();
    		black[u]=1;
    	}
    	for(int i=1;i<n;i++) {
    		int x=read(),y=read(),v=read();
    		add(x,y,v),add(y,x,v);
    	}
    	work(1,n),printf("%d
    ",ans);
    	return 0;
    }
    

    边分治版代码如下:

    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef pair<int,int> pii;
    #define fr first
    #define sc second
    
    const int maxn=4e5+5,inf=2e9+10;
    
    int siz[maxn],f[maxn];
    bool vis[maxn],black[maxn];
    int n,k,m,tot,ans,cnt,mx,id,N,len;
    int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
    
    vector<pii>to[maxn];
    vector<pii>::iterator it;
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    void add(int a,int b,int c) {
    	pre[++tot]=now[a];
    	now[a]=tot,son[tot]=b,val[tot]=c;
    }
    
    void find_son(int fa,int u) {
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(v!=fa)to[u].push_back(make_pair(v,val[p])),find_son(u,v);
    }
    
    void rebuild() {
    	tot=1;memset(now,0,sizeof(now));
    	for(int i=1;i<=cnt;i++) {
    		int size=to[i].size();
    		if(size<=2) {
    			for(it=to[i].begin();it!=to[i].end();it++) {
    				pii tmp=*it;
    				add(i,tmp.fr,tmp.sc),add(tmp.fr,i,tmp.sc);
    			}
    		}
    		else {
    			pii u1=make_pair(++cnt,0),u2;
    			if(size==3)u2=to[i].front();
    			else u2=make_pair(++cnt,0);
    			add(i,u1.fr,u1.sc),add(u1.fr,i,u1.sc);
    			add(i,u2.fr,u2.sc),add(u2.fr,i,u2.sc);
    			if(size==3) {
    				for(int j=1;j<=2;j++)
    					to[cnt].push_back(to[i].back()),to[i].pop_back();
    			}
    			else {
    				int p=0;
    				for(it=to[i].begin();it!=to[i].end();it++) {
    					if(!p)to[cnt].push_back(*it);
    					else to[cnt-1].push_back(*it);p^=1;
    				}
    			}
    		}
    	}
    }
    
    void find_edge(int fa,int u) {
    	siz[u]=1;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[p>>1]&&v!=fa) {
    			find_edge(u,v);
    			siz[u]+=siz[v];
    			if(abs(N-2*siz[v])<mx)
    				mx=abs(N-2*siz[v]),id=p>>1;
    		}
    }
    
    void solve(int fa,int u,int dis,int cnt) {
    	if(cnt<=k)f[cnt]=max(f[cnt],dis);siz[u]=1;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[p>>1]&&v!=fa)solve(u,v,dis+val[p],cnt+black[v]),siz[u]+=siz[v];
    }
    
    void query(int fa,int u,int dis,int cnt) {
    	if(cnt<=k)ans=max(ans,dis+f[min(mx,k-cnt)]+len);siz[u]=1;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[p>>1]&&v!=fa)query(u,v,dis+val[p],cnt+black[v]),siz[u]+=siz[v];
    }
    
    int dfs(int fa,int u,int cnt) {
    	int res=cnt;
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[p>>1]&&v!=fa)res=max(res,dfs(u,v,cnt+black[v]));
    	return res;
    }
    
    void work(int u,int size) {
    	if(size<2)return;
    	N=size,mx=id=cnt+1,find_edge(0,u);
    	vis[id]=1;len=val[id<<1];
    	int u1=son[id<<1],u2=son[id<<1|1];
    	mx=min(k,max(dfs(0,u1,black[u1]),dfs(0,u2,black[u2])));
    	for(int i=0;i<=mx;i++)f[i]=-inf;
    	solve(0,u1,0,black[u1]);
    	for(int i=1;i<=mx;i++)
    		f[i]=max(f[i],f[i-1]);
    	query(0,u2,0,black[u2]);
    	work(u1,siz[u1]),work(u2,siz[u2]);
    }
    
    int main() {
    	cnt=n=read(),k=read(),m=read();
    	for(int i=1;i<=m;i++) {
    		int x=read();
    		black[x]=1;
    	}
    	for(int i=1;i<n;i++) {
    		int x=read(),y=read(),v=read();
    		add(x,y,v),add(y,x,v);
    	}
    	find_son(0,1),rebuild();
    	work(1,cnt),printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    【无旋转treap】模板
    线性选择算法好题
    【codeforces】305C GCD,容斥
    双连通
    线段树(3)
    线段树(2)
    线段树
    2015 Multi-University Training Contest 2
    2015 Multi-University Training Contest 1
    Codeforces Round #302 (Div. 1)
  • 原文地址:https://www.cnblogs.com/AKMer/p/10046954.html
Copyright © 2011-2022 走看看