zoukankan      html  css  js  c++  java
  • noip 2018 D1T3 赛道修建

    noip 2018 D1T3 赛道修建

    首先考虑二分答案,这时需要的就是对于一个长度求出能在树中选出来的最多的路径条数。考虑到一条路径是由一条向上的路径与一条向下的路径构成,或者仅仅是向上或向下的路径构成。

    (f_i)为i这颗子树中最多能选出来多少条路径,(g_i)为在i这颗子树内选出来(f_i)条路径后最多能往下延伸多么长的距离,就是以i点为端点向i的子树内可以选出来的最长的路径。

    考虑一颗以i为根的子树,首先(f_i=sum_{jin the son of i}f_j),然后对i的所有儿子的(g_j+w_{i,j})排序,如果该值大于二分的答案,则直接将这个看成一条单独的路径一定不会更劣,那么直接将(f_i)加一。

    然后从小到大枚举这个值,找到另一个最小的未被使用过的值使得两个数相加大于答案,并删掉这两个值。如果找不到这样的值,则用枚举的这个值更新(g_i)

    实现的时候可以用multiset,时间复杂度大概是(O(nlog^2n)?)

    但是我的常数好大啊,在洛谷上开个O2就过了

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<set>
    using namespace std;
    
    const int Maxn=110000;
    
    int to[Maxn],w[Maxn],nxt[Maxn],first[Maxn],f[Maxn],g[Maxn],tot=1;
    int n,m,mid,u,v,wi;
    
    inline void add(int u,int v,int wi) {
    	to[tot]=v;
    	w[tot]=wi;
    	nxt[tot]=first[u];
    	first[u]=tot++;
    	to[tot]=u;
    	w[tot]=wi;
    	nxt[tot]=first[v];
    	first[v]=tot++;
    }
    
    void work(int root,int fa) {
    	f[root]=g[root]=0;
    	multiset<int>se;
    	vector<int>vi;
    	for(int i=first[root];i;i=nxt[i])
    		if(to[i]!=fa) {
    			work(to[i],root);
    			int temp=g[to[i]]+w[i];
    			f[root]+=f[to[i]];
    			if(temp>=mid) {
    				f[root]++;
    				continue;
    			}
    			vi.push_back(temp);
    			se.insert(temp);
    		}
    	sort(vi.begin(),vi.end());
    	for(vector<int>::iterator i=vi.begin();i!=vi.end();i++) 
    		if(se.count(*i)) {
    			se.erase(se.find(*i));
    			multiset<int>::iterator j=se.lower_bound(mid-*i);
    			if(j==se.end())
    				g[root]=*i;
    			else {
    				f[root]++;
    				se.erase(j);
    			}
    		}
    }
    
    int main() {
    //	freopen("test.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<n;i++) {
    		scanf("%d%d%d",&u,&v,&wi);
    		add(u,v,wi);
    	}
    	int l=1,r=0x3f3f3f3f;mid=100000;int ans=1;
    	work(1,1);
    	if(f[1]<m) r=99999;
    	else l=100000,ans=100000;
    	while(l<=r) {
    //		memset(f,0,sizeof(f));
    //		memset(g,0,sizeof(g));
    		work(1,1);
    		if(f[1]>=m) {
    			ans=mid;
    			l=mid+1;
    		}
    		else r=mid-1;
    		mid=l+r>>1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    新建一个类并绑定一个activity
    关于fragment保存变量的问题
    关于使用别人方法的效率问题
    使用asynctask的问题
    关于整个头像更新问题(2)
    关于查找所需代码的问题
    静态变量的使用问题
    关于更换头像的整个过程理解
    fill_parent 和 match_parent区别
    关于ui修改的若干想法
  • 原文地址:https://www.cnblogs.com/shanxieng/p/9972059.html
Copyright © 2011-2022 走看看