zoukankan      html  css  js  c++  java
  • SPOJ 913 Query on a tree II

    spoj题面

    Time limit 433 ms //spoj的时限都那么奇怪

    Memory limit 1572864 kB //1.5个G,疯了

    Code length Limit 15000 B

    OS Linux

    Language limit All except: ERL JS-RHINO NODEJS PERL6 VB.NET

    Source Special thanks to Ivan Krasilnikov for his alternative solution

    Author Thanh-Vy Hua

    吐槽

    期中考第二个能A的题,结果写了半个小时,调了一个半小时……纪念一下第二道被多组Case读入害死的题目……这题是把下一个case的前两个数读进来了,那一次是这次的case没读完就进入下一个case了,这才过了两个星期啊……下次测这种样例就该把样例多复制几遍……

    顺便吐槽,这是啥数据啊……一步一步地向上暴力跳都能A,亏我还跳重链……而且case之间加空行这个要求它不会判,加不加空行都是AC

    解题思路

    我一看见这题就没多想,直接上树链剖分,结果写完反应过来杀鸡用牛刀,这题dfs预处理一遍,倍增跳树并统计结果是更优的办法,代码可以短不少。

    树链剖分处理(KTH)操作是利用了一个性质:一条重链上的节点新id,从上到下是连续的,所以跳链的时候如果发现跳过了目标(k),那么就直接使用这条重链两端的新(id)计算出目标(k)(id),然后转换成原来的的编号即可。另外,由于这题路径有了方向,所以跳链的时候不能像平时那样std::swap(u,v),而是要判断应该跳u还是跳v,然后分情况写。

    源代码

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    int T;
    int n;
    
    struct Edge{
    	int nxt,to,w;
    }e[200010];
    int head[100010],cnt;
    void add(int u,int v,int w)
    {
    	e[cnt]={head[u],v,w};
    	head[u]=cnt++;
    	e[cnt]={head[v],u,w};
    	head[v]=cnt++;
    }
    
    struct Tree{
    	int fa,dep,w,sz,wson;
    	int top,id;
    }t[100010];
    void dfs1(int fa,int u,int w)
    {
    	t[u].fa=fa;
    	t[u].dep=t[fa].dep+1;
    	t[u].w=w;
    	t[u].sz=1;
    	int maxn=0;
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to;
    		if(v==fa) continue;
    		dfs1(u,v,e[i].w);
    		int temp=t[v].sz;
    		t[u].sz+=temp;
    		if(temp>maxn)
    		{
    			maxn=temp;
    			t[u].wson=v;
    		}
    	}
    }
    int id=1;
    int mp[100010];//用新id查询老id
    long long a[100010];
    void dfs2(int u,int top)
    {
    	t[u].top=top;
    	t[u].id=id;
    	a[id]=t[u].w;
    	mp[id]=u;
    	id++;
    	if(t[u].sz==1) return;
    	dfs2(t[u].wson,top);
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to;
    		if(v==t[u].fa||v==t[u].wson) continue;
    		dfs2(v,v);
    	}
    }
    
    long long sum[400010];//线段树维护区间和
    void build(int x,int l,int r)
    {
    	if(l==r)
    	{
    		sum[x]=a[l];
    		return;
    	}
    	int mid=l+r>>1;
    	build(x<<1,l,mid);
    	build(x<<1|1,mid+1,r);
    	sum[x]=sum[x<<1]+sum[x<<1|1];
    }
    long long quesum(int x,int l,int r,int ql,int qr)
    {
    	if(qr<ql) return 0;
    	if(ql<=l&&r<=qr) return sum[x];
    	int mid=l+r>>1;
    	long long ans=0;
    	if(ql<=mid) ans+=quesum(x<<1,l,mid,ql,qr);
    	if(qr>mid) ans+=quesum(x<<1|1,mid+1,r,ql,qr);
    	return ans;
    }
    
    long long dist(int u,int v)
    {
    	long long ans=0;
    	while(t[u].top!=t[v].top)
    	{
    		if(t[t[u].top].dep<t[t[v].top].dep) std::swap(u,v);
    		ans+=quesum(1,1,n,t[t[u].top].id,t[u].id);
    		u=t[t[u].top].fa;
    	}
    	if(t[u].id>t[v].id) std::swap(u,v);
    	ans+=quesum(1,1,n,t[u].id+1,t[v].id);
    	return ans;
    }
    
    int kth(int ss,int tt,int k)//要区分起点和终点
    {
    	int num=0;
    	int u=ss,v=tt;//先跳一遍找到总的点数
    	while(t[u].top!=t[v].top)
    	{
    		if(t[t[u].top].dep<t[t[v].top].dep) std::swap(u,v);
    		num+=t[u].id-t[t[u].top].id+1;//这条重链
    		u=t[t[u].top].fa;
    	}
    	if(t[u].id>t[v].id) std::swap(u,v);
    	num+=t[v].id-t[u].id+1;
    	while(t[ss].top!=t[tt].top)//跳链找答案
    	{
    		if(t[t[ss].top].dep<t[t[tt].top].dep)//跳tt点
    		{
    			int temp=t[tt].id-t[t[tt].top].id+1;//重链长度
    			if(num-temp<k)//跳过了
    			{
    				return mp[t[tt].id-(num-k)];
    			}
    			else//还在上方,num减少
    			{
    				num-=temp;
    				tt=t[t[tt].top].fa;
    			}
    		}
    		else//跳ss点
    		{
    			int temp=t[ss].id-t[t[ss].top].id+1;
    			if(k<=temp)//找到了
    			{
    				return mp[t[ss].id-k+1];
    			}
    			else//还在上方
    			{
    				k-=temp;
    				num-=temp;
    				ss=t[t[ss].top].fa;
    			}
    		}
    	}
    	//跳到同一条链上了。
    	if(t[ss].dep<t[tt].dep)
    		return mp[t[ss].id+k-1];
    	else
    		return mp[t[ss].id-k+1];
    }
    
    int main()
    {
    	//freopen("test.in","r",stdin);
    	scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d",&n);
    		memset(head,0,sizeof(int)*(n+2));
    		cnt=id=1;
    		for(int i=1,u,v,w;i<n;i++)
    		{
    			scanf("%d%d%d",&u,&v,&w);
    			add(u,v,w);
    		}
    		dfs1(0,1,0);
    		dfs2(1,1);
    		build(1,1,n);
    		while(1)
    		{
    			char opt[10];
    			int u,v,k;
    			scanf("%s",opt);//开始我写了个%s%d%d,opt,&u,&v……
    			if(opt[1]=='I')
    			{
    				scanf("%d%d",&u,&v);
    				printf("%lld
    ",dist(u,v));
    			}
    			else if(opt[1]=='T')
    			{
    				scanf("%d%d%d",&u,&v,&k);
    				printf("%d
    ",kth(u,v,k));
    			}
    			else break;
    		}
            puts("");//空行加不加都可以A
    	}
    	return 0;
    }
    
  • 相关阅读:
    替换空格
    centos虚拟机 服务器搭建
    Java 深度遍历和广度优先遍历
    idea热部署Devtools
    idea字符编码设置
    idea破解详细教程
    Java序列化
    60+Git常用命令行
    LeetCode 236. 二叉树的最近公共祖先
    08 讲解v-cloak,v-text,v-html的基本使用
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/11320522.html
Copyright © 2011-2022 走看看