• 【GDOI2014模拟】雨天的尾巴


    题目

    深绘里一直很讨厌雨天。
    灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
    虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连
    根拔起,以及田地里的粮食被弄得一片狼藉。
    无奈的深绘里和村民们只好等待救济粮来维生。
    不过救济粮的发放方式很特别。
    首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择
    两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。
    然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

    分析

    相信大多数人都跟我一样,看到这道题,果断认为是树链剖分。
    其实不然(我1.5h把熟练剖分打出来,以为能过50%。结果打错了,20%。然后。。。打暴力的人都是50%,#%……&*)。
    我们注意到z<=10^9,那么的数据范围。
    但是m<=100000,所以就先做个离散化。
    。。。
    接着用到个很神奇的东西,叫线段树合并。
    对于树上的每一个节点开一棵线段树,记录该节点的每个z的数量以及某一段z的最大值。注意要动态开节点,否则会爆空间。
    发现,如果要修改从
    xy的路径,其实就是将x的z值加一,y的z值加一,lca(x,y)的z值减一以及fa[lca(x,y)]的z值减一(why?因为当线段树合并后从xlca(x,y)的路径上和从ylca(x,y)的路径上的每个节点的z值都加一,但lca(x,y)这个节点重复加了,那么就减掉。不过剩余的z值还会继续上传,所以在fa[lca(x,y)]**就把上传的z值减去)
    合并操作

    int mesh(int x,int y,int l,int r)
    {
    	if(l==r)
    	{
    		tree[x].v+=tree[y].v;
    		return 0;                                                    
    	}
    	int mid=(l+r)/2;
    	if(tree[y].l)
    	{
    		if(!tree[x].l)
    			tree[x].l=tree[y].l;
    			else
    		mesh(tree[x].l,tree[y].l,l,mid);
    	}
    	if(tree[y].r)
    	{
    		if(!tree[x].r)
    			tree[x].r=tree[y].r;
    			else
    		mesh(tree[x].r,tree[y].r,mid+1,r);
    	}
    	tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
    }
    

    当然,当所有的修改操作做完后才线段树合并,否则会超时。


    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const int maxlongint=2147483647;
    using namespace std;
    struct trees
    {
    	int l,r,v;
    }tree[8000000];
    struct read
    {
    	int x,y,z;
    }re[110000];
    int next[201000],last[201000],to[201000],po,tot,n,m,g[200000][20],t[200000],deep[200000],f[200000],sum,ans[2000000];
    bool cmp(read x,read y)
    {
    	return x.z<y.z;
    }
    int bj(int x,int y)
    {
    	next[++tot]=last[x];
    	last[x]=tot;
    	to[tot]=y;
    }
    int dg(int x)
    {
    	for(int i=last[x];i;i=next[i])
    	{
    		int j=to[i];
    		if(j!=g[x][0])
    		{
    			deep[j]=deep[x]+1;
    			g[j][0]=x;
    			dg(j);
    		}
    	}
    }
    int prelca()
    {
    	for(int j=1;j<=log2(n);j++)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			g[i][j]=g[g[i][j-1]][j-1];
    		}
    	}
    }
    int lca(int x,int y)
    {
    	if(deep[x]>deep[y])
    	{
    		x=x^y;
    		y=x^y;
    		x=x^y;
    	}
    	for(int i=log2(n);i>=0;i--)
    	{
    		if(deep[g[y][i]]>deep[x])
    			y=g[y][i];
    	}
    	if(deep[y]!=deep[x]) y=g[y][0];
    	for(int i=log2(n);i>=0;i--)
    	{
    		if(g[y][i]!=g[x][i])
    		{
    			y=g[y][i];
    			x=g[x][i];
    		}
    	}
    	if(x!=y) y=g[y][0];
    	return y;
    }
    int put(int v,int l,int r,int x,int y)
    {
    	if(l==r)
    	{
    		tree[v].v+=y;
    		return 0;
    	}
    	int mid=(l+r)/2;
    	if(x<=mid)
    	{
    		if(!tree[v].l)
    			tree[v].l=++sum;
    		put(tree[v].l,l,mid,x,y);
    	}
    	else
    	{
    		if(!tree[v].r)
    			tree[v].r=++sum;
    		put(tree[v].r,mid+1,r,x,y);	
    	}
    	tree[v].v=max(tree[tree[v].l].v,tree[tree[v].r].v);
    }
    int work(int x,int y,int z)
    {
    	int lc=lca(x,y);
    	if(!f[x])
    	{
    		f[x]=++sum;
    	}
    	put(f[x],1,tot,z,1);
    	if(!f[y])
    	{
    		f[y]=++sum;
    	}
    	put(f[y],1,tot,z,1);
    	if(!f[lc])
    	{
    		f[lc]=++sum;
    	}
    	put(f[lc],1,tot,z,-1);
    	if(g[lc][0])
    	{
    		if(!f[g[lc][0]]) f[g[lc][0]]=++sum;
    		put(f[g[lc][0]],1,tot,z,-1);
    	}
    }
    int mesh(int x,int y,int l,int r)
    {
    	if(l==r)
    	{
    		tree[x].v+=tree[y].v;
    		return 0;                                                    
    	}
    	int mid=(l+r)/2;
    	if(tree[y].l)
    	{
    		if(!tree[x].l)
    			tree[x].l=tree[y].l;
    			else
    		mesh(tree[x].l,tree[y].l,l,mid);
    	}
    	if(tree[y].r)
    	{
    		if(!tree[x].r)
    			tree[x].r=tree[y].r;
    			else
    		mesh(tree[x].r,tree[y].r,mid+1,r);
    	}
    	tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
    }
    int find(int v,int l,int r)
    {
    	if(l==r)
    	{
    		return l;
    	}
    	if(tree[tree[v].l].v==0 && tree[tree[v].r].v==0)
    		return 0;
    	int mid=(l+r)/2;
    	if(tree[tree[v].l].v>=tree[tree[v].r].v)
    	{
    		return find(tree[v].l,l,mid);
    	}
    	else
    	{
    		return find(tree[v].r,mid+1,r);	
    	}
    }
    int merge(int x)
    {
    	for(int i=last[x];i;i=next[i])
    	{
    		int j=to[i];
    		if(j!=g[x][0])
    		{
    			merge(j);
    			if(!f[x])
    			{		
    				f[x]=++sum;
    			}	
    			if(!f[j])
    			{		
    				f[j]=++sum;
    			}
    			mesh(f[x],f[j],1,tot);
    		}
    	}
    	ans[x]=find(f[x],1,tot);
    }
    int main()
    {
    	scanf("%d",&n);
    	scanf("%d",&m);
    	for(int i=1;i<=n-1;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		bj(x,y);
    		bj(y,x);
    	}
    	deep[1]=1;
    	dg(1);
    	prelca();
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&re[i].x,&re[i].y,&re[i].z);
    	}
    	sort(re+1,re+m+1,cmp);
    	tot=0;
    	sum=0;
    	for(int i=1;i<=m;i++)
    	{
    		if(re[i].z==t[tot])
    		{
    			re[i].z=tot;
    		}
    		else
    		{
    			t[++tot]=re[i].z;
    			re[i].z=tot;
    		}
    	}
    	for(int i=1;i<=m;i++)
    	{
    		work(re[i].x,re[i].y,re[i].z);
    	}
    	merge(1);
    	for(int i=1;i<=n;i++)
    	{
    		if(!f[i])
    		{		
    			f[i]=++sum;
    		}	
    		printf("%d
    ",t[ans[i]]);
    	}
    }
    
    
    
  • 相关阅读:
    第七周java学习总结
    第六周java学习总结
    20175206迭代与JDB测试
    第五周java学习总结
    实验一 Java开发环境的熟悉(Linux + Eclipse)
    第四周java学习总结
    第三周java学习总结
    es6零基础学习之项目目录创建(一)
    软键盘影响页面布局之定位
    当input的框全部不为空时,提交按钮变色
  • 原文地址:https://www.cnblogs.com/chen1352/p/9026702.html
Copyright © 2020-2023  润新知