JZOJ 5984. 【北大2019冬令营模拟2019.1.1】仙人掌

Description

Description

Input

Input

Output

Output

Sample Input

5 10
1 2
2 3
2 4
3 5
1
5
2
4
3
5
4
2
3
1

Sample Output

2060

Data Constraint

Data Constraint

Solution

  • 考虑暴力的根号算法,先将原树定为一颗有根树。

  • 修改时先考虑其子节点,可以发现不同的权值最多有 m \sqrt m m 种(最坏情况是 1 , 2 , 3 , 4 … … 1,2,3,4…… 1,2,3,4

  • 于是我们用一个链表存每个点的儿子节点不同种类的值及该值的个数。

  • 对儿子节点的修改就是 O ( m ) O(\sqrt m) O(m ) 的。

  • 那么修改它的父亲节点,我们只需找其父亲的父亲,再改儿子就可以了。

  • 这里涉及到如何 O ( 1 ) O(1) O(1) 快速查询一个点的现时权值,这个打两个标记就可以了:

  • 若修改点是 x x x ,那么我们令 t a g 1 [ x ] + + , t a g 2 [ f a [ x ] ] + + tag1[x]++ , tag2[fa[x]]++ tag1[x]++,tag2[fa[x]]++ ,来表示修改情况。

  • 那么一个点 y y y 的现时权值就是 t a g 1 [ f a [ y ] ] + t a g 2 [ y ] tag1[fa[y]]+tag2[y] tag1[fa[y]]+tag2[y]

  • 于是我们也能在 O ( m ) O(\sqrt m) O(m ) 的复杂度内完成其父亲节点的修改。

  • 这个算法的时间复杂度上限为 O ( m m ) O(m\sqrt m) O(mm ) ,但实际上远达不到上限。

Code

#include<cstdio>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=5e5+5,M=N*80,mo=1000109107;
int tot,sum;
int first[N],nex[N<<1],en[N<<1];
int first1[N],nex1[M],en1[M],w[M];
int fa[N],son[N],t1[N],t2[N];
inline int read()
{
	int X=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
inline void insert(int x,int y)
{
	nex[++tot]=first[x];
	first[x]=tot;
	en[tot]=y;
}
void dfs(int x)
{
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x])
		{
			fa[en[i]]=x;
			son[x]++;
			dfs(en[i]);
		}
}
int main()
{
	freopen("cactus.in","r",stdin);
	freopen("cactus.out","w",stdout);
	int n=read(),m=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		insert(x,y);
		insert(y,x);
	}
	dfs(1);
	fa[1]=n+1;
	son[n+1]=1;
	tot=0;
	for(int i=1;i<=n+1;i++)
	{
		nex1[++tot]=first1[i];
		first1[i]=tot;
		en1[tot]=0;
		w[tot]=son[i];
	}
	for(int j=1;j<=m;j++)
	{
		int x=read();
		for(int i=first1[x];i;i=nex1[i]) en1[i]++;
		int ans=0;
		if(x>1)
		{
			int y=fa[fa[x]],num=t2[fa[x]]+t1[y];
			for(int i=first1[y],last=0;i;last=i,i=nex1[i])
				if(en1[i]==num)
				{
					if(w[i]>1)
					{
						w[i]--;
						break;
					}
					if(i==first1[y])
					{
						first1[y]=nex1[i];
					}else
					{
						nex1[last]=nex1[i];
					}
					break;
				}
			num++;
			bool pd=false;
			for(int i=first1[y];i;i=nex1[i])
				if(en1[i]==num)
				{
					w[i]++;
					pd=true;
					break;
				}
			if(!pd)
			{
				nex1[++tot]=first1[y];
				first1[y]=tot;
				en1[tot]=num;
				w[tot]=1;
			}
			ans=num;
		}
		t1[x]++;
		t2[fa[x]]++;
		for(int i=first1[x];i;i=nex1[i])
			if(w[i]&1) ans^=en1[i];
		ans=ans*((LL)j*j%mo+j)%mo;
		sum=(sum+ans)%mo;
	}
	printf("%d",sum);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值