• 【NOIP2018】赛道修建(考场)


    题意:
    在一棵树上选出m条路径,这些路径不能共用任意一条边
    问这些路径中最小的一条路最大是多少
    

    这道题的部分分十分有意思,搞了一搞

    第一种情况是m=1,即只选出一条最长的路径
    显然是计算树的直径

    int fa[N],ans=0;
    int dfs1(int u)
    {
    	int sum1=0,sum2=0;
    	for(register int i=head[u];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa[u]) continue;
    		fa[v]=u;
    		sum2=max(sum2,dfs1(v)+edge[i].dis);
    		if(sum2>sum1) swap(sum2,sum1);
    	}
    	ans=max(ans,sum1+sum2);
    	return sum1;
    }
    

    ans即为所求

    第二种情况是地图没有分支,为一条链
    那就是说在序列上面二分,很经典的题了

    bool check(int x)
    {
    	int d=0,now=0;
    	for(register int i=1;i<n;++i)
    	{
    		if(now+a[i]>=x)
    		{
    			now=0;
    			d++; 
    		}
    		else now+=a[i];
    	}
    	return d>=m;
    }
    

    第三种情况是菊花图,所有的u=1
    直接每次枚举最长加最短,次长加次短,……,统计最小值即可

    if(juhua)//菊花图直接判断即可 
    {
    	for(register int i=head[1];i;i=edge[i].next)
    	{
    		int vv=edge[i].to;
    		a[vv-1]=edge[i].dis;
    	}
    	sort(a+1,a+n,cmp);
    	int ans=0x3f3f3f3f;
    	for(register int i=1;i<=m;++i)
    		ans=min(ans,a[i]+a[2*m-i+1]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    所以总的代码是这样

    #include<bits/stdc++.h>
    #define N 50005
    #define mid ((l+r+1)>>1)
    using namespace std;
    
    int n,m,u,v,w;
    
    struct Edge
    {
    	int next,to,dis;
    }edge[N<<1];
    int cnt=0,head[N];
    
    inline void add_edge(int from,int to,int dis)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	edge[cnt].dis=dis;
    	head[from]=cnt;
    }
    
    template<class T>inline void read(T &res)
    {
    	char c;T flag=1;
    	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    
    int fa[N],ans=0;
    int dfs1(int u)
    {
    	int sum1=0,sum2=0;
    	for(register int i=head[u];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa[u]) continue;
    		fa[v]=u;
    		sum2=max(sum2,dfs1(v)+edge[i].dis);
    		if(sum2>sum1) swap(sum2,sum1);
    	}
    	ans=max(ans,sum1+sum2);
    	return sum1;
    }
    
    int a[N];
    void dfs2(int u)
    {
    	for(register int i=head[u];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa[u]) continue;
    		fa[v]=u;
    		dfs2(v);
    		a[u]=edge[i].dis;
    	}
    }
    
    bool check(int x)
    {
    	int d=0,now=0;
    	for(register int i=1;i<n;++i)
    	{
    		if(now+a[i]>=x)
    		{
    			now=0;
    			d++; 
    		}
    		else now+=a[i];
    	}
    	return d>=m;
    }
    
    bool cmp(int a,int b) {return a>b;}
    
    int main()
    {
    //	freopen("testdata.in","r",stdin);
    	read(n);read(m);
    	bool juhua=1,lian=1;
    	int sum=0;
    	for(register int i=1;i<=n-1;++i)
    	{
    		read(u);read(v);read(w);
    		sum+=w;
    		if(u!=1) juhua=0;
    		if(v!=u+1) lian=0;
    		add_edge(u,v,w);
    		add_edge(v,u,w);
    	}
    	if(m==1)//只有一条路,算直径 
    	{
    		dfs1(1);
    		printf("%d
    ",ans);
    		return 0;
    	}
    	if(lian)//地图是一条链,相当于序列二分答案 
    	{
    		dfs2(1);
    		int l=1,r=sum;
    		while(l<=r)
    		{
    //			cout<<l<<" "<<r<<" "<<mid<<endl;
    			if(check(mid)) l=mid;
    			else r=mid-1;
    		}
    		printf("%d
    ",l);
    		return 0;
    	}
    	if(juhua)//菊花图直接判断即可 
    	{
    		for(register int i=head[1];i;i=edge[i].next)
    		{
    			int vv=edge[i].to;
    			a[vv-1]=edge[i].dis;
    		}
    		sort(a+1,a+n,cmp);
    		int ans=0x3f3f3f3f;
    		for(register int i=1;i<=m;++i)
    			ans=min(ans,a[i]+a[2*m-i+1]);
    		printf("%d
    ",ans);
    		return 0;
    	}
    	return 0;
    }
    

    这么写的话就已经有55分了,性价比极高……
    所以D1你就有了100+100+55=255的高分

    剩下的时间可以拿来思考正解
    正解其实也十分简单,等会儿写……

    update:正解已迁移至这里:点我

  • 相关阅读:
    Eclipse常用快捷键
    java中构造方法及其作用
    jsp的验证码实现
    request.getParameter()与request.setAttribute()的区别 (转载)
    HTML表单操作的记录
    Java Collection(转载)
    Java中StringBuffer类append方法的使用
    java中string.trim()函数的使用
    doGet与doPost的区别
    celery
  • 原文地址:https://www.cnblogs.com/tqr06/p/11736857.html
Copyright © 2020-2023  润新知