zoukankan      html  css  js  c++  java
  • BZOJ1758:[WC2010]重建计划

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

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1758

    先来讲讲部分分吧。

    对于(20)%的数据

    我们可以对于每个点为根(dfs)一遍,然后用(deep)([L,R])之间的点与当前根的距离除以深度来更新答案;

    时间复杂度:(O(n^2))

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

    对于另外(30)%的数据

    因为是一条链,我们可以把(n-1)条边拆出来放到一个数组里,就相当于找一个长度在([L,R])之间的子段平均值最大。一看到平均值我们就可以想到二分。我们二分一个(limit),然后把每条边都减去(limit),这个时候问题就转化成了怎么去检查这个数组有没有子段长度在([L,R])内,并且子段和大于(0)。子段和我们可以用前缀和相减来表示,用单调上升队列维护一下即可。队头就是在所有与当前点距离为([L,R])之内的点,前缀和最小的那一个。用当前点的前缀和减去队头点的前缀和判断是否大于等于(0)即可。

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

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

    部分分代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int maxn=1e5+5;
    const double eps=1e-6;
    
    bool line;
    double ans;
    double sum[maxn];
    int n,L,R,tot,rt;
    int a[maxn],deg[maxn];
    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;
    }
    
    void add(int a,int b,int c) {
        pre[++tot]=now[a];
        now[a]=tot,son[tot]=b,val[tot]=c;
    }
    
    void dfs(int fa,int u,int dep,ll dis) {
        if(dep>R)return;
        if(L<=dep&&dep<=R)ans=max(ans,1.0*dis/dep);
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(v!=fa)dfs(u,v,dep+1,dis+val[p]);
    }
    
    void solve1() {
        for(int i=1;i<=n;i++)
            dfs(0,i,0,0);
        printf("%.3lf
    ",ans);
    }
    
    void make_a(int fa,int u) {
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(v!=fa)a[++n]=val[p],make_a(u,v);
    }
    
    struct data {
        int pos;
        double v;
    
        data() {}
    
        data(int _pos,double _v) {
            pos=_pos,v=_v;
        }
    }list[maxn];
    
    bool check(double limit) {
        int h=0,t=0;
        for(int i=1;i<=n;i++)
            sum[i]=sum[i-1]+1.0*a[i]-limit;
        for(int i=L;i<=n;i++) {
            while(h!=t&&i-list[h].pos>R)h++;
            while(t!=h&&list[t-1].v>sum[i-L])t--;
            list[t++]=data(i-L,sum[i-L]);
            if(sum[i]-list[h].v>0)return 1;
        }
        return 0;
    }
    
    void solve2() {
        n=0;make_a(0,rt);
        for(int i=1;i<=n;i++)
            sum[i]=sum[i-1]+a[i];
        double l=0,r=sum[n];
        while(l<r-eps) {
            double mid=(l+r)/2;
            if(check(mid))l=mid;
            else r=mid;
        }
        printf("%.3lf
    ",l);
    }
    
    int main() {
        n=read(),L=read(),R=read();
        for(int i=1;i<n;i++) {
            int a=read(),b=read(),c=read();
            add(a,b,c),add(b,a,c);deg[a]++,deg[b]++;
        }
        for(int i=1;i<=n;i++) {
            if(deg[i]>2)line=1;
            if(deg[i]==1)rt=i;
        }
        if(n<=5000)solve1();//20%
        else if(!line)solve2();//另外30%
        return 0;
    }
    

    部分分已经提示了正解了。对于这题,我们可以二分一个答案,然后让每条边减去这个值,判断树里面有没有一条长度在([L,R])之内的路径权值和大于等于(0)。同样的,我们也可以用单调队列维护。

    一开始我觉得不好做,因为我(dfs)子树去更新答案的话还要还原单调队列。然后就傻傻地开栈记录被弹出队列的东西……假设当前点的子树做完之后再把被当前点弹出去的东西塞回去……然后(TLE)

    后来看了(hzw)的做法,把(dfs)改成(bfs),深度就会递增了,就不需要还原单调队列了。洛谷可以过,但是(BZOJ)过不了。没办法了,只能自己卡常了。

    我们设(f_i)表示在已经遍历过的子树中长度为(i)的路径最大值是多少,(g_i)是当前要询问的子树内长度为(i)的路径最大值是多少,然后就直接用(f)(g)两个数组跑单调队列就行了。另外还可以用到SPOJ1825 Free Tour II里的优化,把子树遍历顺序按照最深的深度从小到大排序,在(BZOJ)就可以过了。

    (BZOJ)神仙还是多,(rk1)是打表的我们不管他,(rk2)居然是个边分治……根本看不懂他代码……

    点分治版代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int maxn=1e5+5;
    const double eps=1e-5;
    
    bool vis[maxn];
    int n,L,R,N,tot,mx,rt;
    int siz[maxn],Q[maxn],V[maxn],depest[maxn];
    double limit,ans,Mx,f[maxn],g[maxn],dis[maxn];
    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;
    }
    
    void add(int a,int b,int c) {
    	pre[++tot]=now[a];
    	now[a]=tot,son[tot]=b,val[tot]=c;
    }
    
    bool cmp(int a,int b) {
    	return depest[a]<depest[b];
    }
    
    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;
    }
    
    void dfs(int fa,int u,int dep,ll len) {
    	siz[u]=1,dis[u]=len,mx=max(mx,dep);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v]&&v!=fa)dfs(u,v,dep+1,len+val[p]),siz[u]+=siz[v];
    }
    
    void make_g(int fa,int u,int dep) {
    	g[dep]=max(g[dep],dis[u]);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[v]&&v!=fa)make_g(u,v,dep+1);
    }
    
    bool query(int a,int b) {
    	for(int i=1;i<=b;i++)f[i]-=limit*i,g[i]-=limit*i;
    	int h=0,t=0,pos=a;bool res=0;
    	for(int i=1;i<=b;i++) {
    		while(i+pos>=L&&pos>=0) {
    			while(h!=t&&f[Q[t-1]]<f[pos])t--;
    			Q[t++]=pos;pos--;
    		}
    		while(h!=t&&Q[h]+i>R)h++;
    		if(h!=t&&f[Q[h]]+g[i]>=0)res=1;
    	}
    	for(int i=1;i<=b;i++)f[i]+=limit*i,g[i]+=limit*i;
    	return res;
    }
    
    bool check(int u) {
    	for(int i=1;i<=depest[V[tot]];i++)
    		f[i]=g[i]=-1e15;
    	for(int i=1;i<=tot;i++) {
    		make_g(u,V[i],1);
    		if(query(depest[V[i-1]],depest[V[i]]))return 1;
    		for(int j=1;j<=depest[V[i]];j++)
    			f[j]=max(f[j],g[j]),g[j]=-1e15;
    	}
    	return 0;
    }
    
    void work(int u,int size) {
    	N=size,rt=mx=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]) {
    			mx=0,dfs(u,v,1,val[p]);
    			depest[v]=mx,V[++tot]=v;
    		}
    	sort(V+1,V+tot+1,cmp);
    	double l=ans,r=Mx;
    	while(l<r-eps) {
    		limit=(l+r)/2;
    		if(check(u))l=limit;
    		else r=limit;
    	}ans=l;
    	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(),L=read(),R=read();
    	for(int i=1;i<n;i++) {
    		int a=read(),b=read(),c=read();
    		add(a,b,c),add(b,a,c);Mx=max(Mx,1.0*c);
    	}work(1,n);printf("%.3lf
    ",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=2e5+5;
    const double eps=1e-5;
    
    bool vis[maxn];
    int n,tot,cnt,mx,id,L,R,N;
    int siz[maxn],Q[maxn],dep[maxn],depest[maxn];
    double ans,limit,Mx,f[maxn],g[maxn],dis[maxn];
    int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
    
    vector<pii>to[maxn];
    vector<pii>::iterator it;
    
    inline 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;
    }
    
    inline void add(int a,int b,int c) {
    	pre[++tot]=now[a];
    	now[a]=tot,son[tot]=b,val[tot]=c;
    }
    
    inline 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);
    }
    
    inline 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-1].push_back(*it);
    					else to[cnt].push_back(*it);p^=1;
    				}
    			}
    		}
    	}
    }
    
    inline 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;
    		}
    }
    
    inline void dfs(int fa,int u,int Dep,double len) {
    	siz[u]=1,dis[u]=len,dep[u]=Dep,mx=max(mx,Dep);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[p>>1]&&v!=fa)dfs(u,v,Dep+(val[p]!=0),len+val[p]),siz[u]+=siz[v];
    }
    
    inline void make(double *a,int fa,int u) {
    	a[dep[u]]=max(a[dep[u]],dis[u]);
    	for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
    		if(!vis[p>>1]&&v!=fa)make(a,u,v);
    }
    
    inline bool query(int a,int b) {
    	for(int i=1;i<=a;i++)f[i]-=limit*i;
    	for(int i=1;i<=b;i++)g[i]-=limit*i;
    	int h=0,t=0,pos=a;bool res=0;
    	for(int i=1;!res&&i<=b;i++) {
    		while(i+pos+(val[id<<1]>0)>=L&&pos>=0) {
    			while(h!=t&&f[Q[t-1]]<f[pos])t--;
    			Q[t++]=pos,pos--;
    		}
    		while(h!=t&&i+Q[h]+(val[id<<1]>0)>R)h++;
    		if(h!=t&&f[Q[h]]+g[i]+val[id<<1]-(val[id<<1]>0)*limit>=0)res=1;
    	}
    	for(int i=1;i<=a;i++)f[i]+=limit*i;
    	for(int i=1;i<=b;i++)g[i]+=limit*i;
    	return res;
    }
    
    inline bool check() {
    	int u1=son[id<<1],u2=son[id<<1|1];
    	if(depest[u1]>depest[u2])swap(u1,u2);
    	for(int i=1;i<=depest[u2];i++)f[i]=g[i]=-1e15;
    	make(f,0,u1),make(g,0,u2);
    	return query(depest[u1],depest[u2]);
    }
    
    inline void work(int u,int size) {
    	if(size<2)return;
    	N=size,mx=id=cnt+1,find_edge(0,u),vis[id]=1;
    	int u1=son[id<<1],u2=son[id<<1|1];
    	mx=0,dfs(0,u1,0,0),depest[u1]=mx;
    	mx=0,dfs(0,u2,0,0),depest[u2]=mx;
    	double l=ans,r=Mx;
    	while(l<r-eps) {
    		limit=(l+r)/2;
    		if(check())l=limit;
    		else r=limit;
    	}ans=l;
    	work(u1,siz[u1]),work(u2,siz[u2]);
    }
    
    int main() {
    	cnt=n=read(),L=read(),R=read();
    	for(int i=1;i<n;i++) {
    		int a=read(),b=read(),c=read();
    		add(a,b,c),add(b,a,c);Mx=max(Mx,1.0*c);
    	}find_son(0,1),rebuild();
    	work(1,cnt);printf("%.3lf
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    关于transition中嵌套keep-alive的问题解决
    vue-cli中使用全局less变量
    git 移除远程仓库关联
    当浏览器窗口大小发生变化时,重新绘制JsPlumb中的线条、端点
    事件循环详解
    React + Sass
    使用OpenSSL自签发SSL证书,支持chrome识别
    英汉翻译技巧之直译与意译、正说与反说、顺序法和逆序法
    英汉翻译技巧之拆句
    英语翻译时句子成分的转换
  • 原文地址:https://www.cnblogs.com/AKMer/p/10073518.html
Copyright © 2011-2022 走看看