zoukankan      html  css  js  c++  java
  • 【*篇】SPOJ FTOUR2 点分治

    淀粉质入门第一道 (现在个人认为spoj比bzoj要好_(:з」∠)_
    关于点分治的话推荐去看一看漆子超的论文>>>这里这里<<<

    之前一直试图入点分治坑, 但是因为种(bu)种(duan)原(tui)因(fei)也没有入...
    结果经常碰到点分治的题目... 然后就各种弃疗...
    不少点分治的题目有非常明显的特征... 通常是给一棵树, 然后问你满足xx条件的路径有多少条/是否存在/最大(小)权值之类的...
    然后点分治的做法也不尽相同 大致能写出如下的伪代码(好吧还是用python高亮)

    def solve(x):        # 处理以x为根的子树
        vis[x]=1           # 把x标记为操作过(视为删掉)
        findroot(x)       # 找到以x为根的子树的重心
        calc(x)             # 统计过x的路径的答案
        for i in son[x]:  # 对于每个儿子
            solve(i)        # 递归处理答案
    

    然后刚入门第一道就不能算是很裸的点分治_(:з」∠)_
    几乎抄了黄学长的代码, 在此表示感谢..
    <题目の传送门>
    hzwer题解の传送门
    题目大意:
    给一棵树, 有(m)个点上有一个标记, 边有边权, 可正可负.
    询问路径上带标记的点不超过(k)的路径的最大权值是多少.

    首先朴素的思路就是暴力嘛... 复杂度(O(n^2))的.. 可能能拿30~40(但这是spoj所以并没有什么部分分..)
    我们要考虑复杂度更好的做法.. 首先根据上面我们说过的特征, 可以看出这题应该可以用点分治做...
    因为后面是递归处理的, 我们只需要考虑如何统计合法的过根节点的路径的答案就行了..


    我们需要处理出(dep[y])(dis[y])两个数组, 分别表示(y)到当前的根节点(以下的根节点均指当前子树的根节点, 因为原来的根节点处理过就删掉了)的路径上的带标记节点个数和路径权值和. 这个可以通过一遍dfs(O(n))完成...
    然后我们考虑有哪些路径会对答案产生影响...
    假如我们要处理(x)(i)个子树, 那么前(i-1)个子树中的点可以与这个子树中的点确定一条路径.
    我们再用一遍(O(n))的dfs处理出(mx[t])这个数组, 表示从前(i-1)棵子树中到根节点的经过(t)个带标记节点的路径的最大长度..
    那我们就可以得到:

    [ans=max{mx[t]+dis[x]} (dep[x]+t<=k) ]

    但是这个dep[x]是会变的, 所以我们可以把儿子按照dep排序一波再做, 就可以顺着推过去了, 据说这样的复杂度是(O(n))级别的..
    然后排序的话总共也就只是(O(nlogn))级别的东西, 配合着点分治的复杂度, 最后就是(O(nlogn))咯..

    代码:

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    #define depth	first
    #define id		second
    const int N=202020;
    inline int gn(int a=0,char c=0,int f=1){
    	for(;(c<48||c>57)&&c!='-';c=getchar());if(c=='-')c=getchar(),f=-1;
    	for(;c>47&&c<58;c=getchar()) a=a*10+c-'0'; return a*f;
    }
    struct edge{
    	int to,next,data;
    }e[N<<1]; int v[N],tot,n,m,k,rt,size,ans;
    void buildedge(int x,int y,int z){
    	e[++tot].to=y; e[tot].next=v[x]; v[x]=tot; e[tot].data=z;
    	e[++tot].to=x; e[tot].next=v[y]; v[y]=tot; e[tot].data=z;
    }
    int q[N],fa[N],son[N],sz[N],mx[N],tmp[N],dep[N],d[N],depmx;
    bool vis[N],a[N];
    void findrt(int x,int fa){
    	sz[x]=1; son[x]=0;
    	for(int i=v[x];i;i=e[i].next)
    		if(!vis[e[i].to]&&e[i].to!=fa){
    			findrt(e[i].to,x);
    			son[x]=max(son[x],sz[e[i].to]);
    			sz[x]+=sz[e[i].to];
    		}
    	son[x]=max(son[x],size-sz[x]);
    	if(son[x]<son[rt]) rt=x;
    }
    void calcdis(int x,int fa){
    	depmx=max(depmx,dep[x]);
    	for(int i=v[x];i;i=e[i].next)
    		if(!vis[e[i].to]&&e[i].to!=fa){
    			dep[e[i].to]=dep[x]+a[e[i].to];
    			d[e[i].to]=d[x]+e[i].data;
    			calcdis(e[i].to,x);
    		}
    }
    void calcmax(int x,int fa){
    	tmp[dep[x]]=max(tmp[dep[x]],d[x]);
    	for(int i=v[x];i;i=e[i].next)
    		if(!vis[e[i].to]&&e[i].to!=fa)
    			calcmax(e[i].to,x);
    }
    vector<pair<int,int> > vec;
    void solve(int x){
    	vis[x]=1; vec.clear(); if(a[x]) --k;
    	for(int i=v[x];i;i=e[i].next)
    		if(!vis[e[i].to]){
    			depmx=0;
    			dep[e[i].to]=a[e[i].to];
    			d[e[i].to]=e[i].data;
    			calcdis(e[i].to,x);
    			vec.push_back(make_pair(depmx,e[i].to));
    		}
    	sort(vec.begin(),vec.end());
    	for(int i=0;i<vec.size();++i){
    		calcmax(vec[i].id,x);
    		int now=0;
    		if(i!=0)
    			for(int j=vec[i].depth;j>=0;--j){
    				while(now+j<k&&now<vec[i-1].depth)
    					++now,mx[now]=max(mx[now],mx[now-1]);
    				if(now+j<=k) ans=max(ans,mx[now]+tmp[j]);
    			}
    		if(i!=vec.size()-1)
    			for(int j=0;j<=vec[i].depth;++j)
    				mx[j]=max(mx[j],tmp[j]),tmp[j]=0;
    		else
    			for(int j=0;j<=vec[i].depth;++j){
    				if(j<=k) ans=max(ans,max(tmp[j],mx[j]));
    				tmp[j]=mx[j]=0;
    			}
    	}
    	if(a[x]) ++k;
    	for(int i=v[x];i;i=e[i].next)
    		if(!vis[e[i].to]){
    			rt=0; size=sz[e[i].to];
    			findrt(e[i].to,x);
    			solve(rt);
    		}
    }
    int main(){
    	n=gn(),k=gn(),m=gn();
    	for(int i=1;i<=m;++i) a[gn()]=1;
    	for(int i=1;i<n;++i){
    		int x=gn(),y=gn(),z=gn();
    		buildedge(x,y,z);
    	}
    	size=son[0]=n; findrt(1,0);
    	solve(rt);
    	printf("%d",ans);
    }
    
  • 相关阅读:
    二分查找
    Uva11464 Even Parity
    Uva10881 Piotr's Ants
    POJ3154 Graveyard
    [NOIP2015] 提高组 洛谷P2680 运输计划
    [NOIP2015] 提高组 洛谷P2679 子串
    [NOIP2015] 提高组 洛谷P2678 跳石头
    [NOIP2015] 提高组 洛谷P2668 斗地主
    [NOIP2015] 提高组 洛谷P2661 信息传递
    [NOIP2015] 提高组 洛谷P2615 神奇的幻方
  • 原文地址:https://www.cnblogs.com/enzymii/p/8476892.html
Copyright © 2011-2022 走看看