zoukankan      html  css  js  c++  java
  • [WC2013] 糖果公园

    链接

    小·清·新 莫队题

    询问和区间内某一种数字的出现次数有关,而且还带修,那大概率就是莫队了。

    但是我们发现这次是链查。而莫队是序列算法,不能在树上移指针。

    所以我们要尝试化树为序列。所以个人感觉叫“树上莫队”是挂羊头卖狗肉

    总结常用的树序列和性质:

    1. dfs序(第一次访问时记录):一个子树内的dfs序为一个连续区间,且一个非叶子节点的某个儿子和它dfs序连续。
    2. 欧拉序(访问一次记录一次):一条链 (u ightarrow v) 上的所有点都在 ([l_u,l_v]) 上出现过,且 (u,v) 的非链上的祖先没有出现过。
    3. 括号序(第一次/最后一次访问时记录):一个点 (u) 到根路径上的所有点在 ([1,l_u]) 中恰好出现一次。

    显然,这题用括号序更加妥当。

    但是括号序的性质是一个点 (u) 到根路径上的点出现一次。那么链 (u ightarrow v)(假设 (l_u<l_v))应该对应 ((l_u,l_v])

    但是手算一下,发现他们的lca也被除掉了。所以再加上就好了。

    那么这样我们就把树转换成序列问题。接下来我们只要统计区间内出现两次的数字即可。这个对于莫队来说没有什么难处。

    那么接下来就是处理修改了。这个也就是套一个带修莫队就行了。

    具体来说,我们需要给每个询问多加一维 (t),即它的统计是在第 (t) 个修改操作后。

    然后我们在移动指针的时候,将修改的指针移动也加进去。即如果一个修改操作在当前询问区间内那么处理修改的贡献,然后再修改。

    理论复杂度 (O(n^{frac 5 3}))但是莫队的复杂度你也信?

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define N 800010
    #define ll long long
    using namespace std;
    int nxt[N<<1],to[N<<1],head[N],cnt;
    void add(int u,int v)
    {
    	nxt[++cnt]=head[u];
    	to[cnt]=v;
    	head[u]=cnt;
    }
    int ld[N],rd[N];
    int qt[N],tot;
    int dep[N],fa[N],son[N],siz[N];
    int top[N];
    void dfs1(int u,int p)
    {
    	dep[u]=dep[p]+1;
    	fa[u]=p;
    	siz[u]=1;
    	qt[ld[u]=++tot]=u;
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v==fa[u]) continue;
    		dfs1(v,u);
    		if(siz[son[u]]<siz[v]) son[u]=v;
    		siz[u]+=siz[v];
    	}
    	qt[rd[u]=++tot]=u;
    }
    void dfs2(int u,int topp)
    {
    	top[u]=topp;
    	if(son[u]) dfs2(son[u],topp);
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v!=fa[u] && v!=son[u]) dfs2(v,v);
    	}
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		u=fa[top[u]];
    	}
    	return dep[u]<dep[v]?u:v;
    }
    int cx[N],cy[N],cp[N],bl[N];
    struct node{
    	int l,r,t,id,ex;
    	bool operator <(const node a)const
    	{
    		if(bl[l]!=bl[a.l]) return bl[l]<bl[a.l];
    		if(bl[r]!=bl[a.r]) return bl[r]<bl[a.r];
    		return t<a.t;
    	}
    }q[N];
    int qtot;
    ll w[N],val[N],ans[N];
    int vis[N],c[N];
    ll wcnt[N],res;
    int fl=1,fr=0,ft=0;
    inline void ins(int x){wcnt[x]++; res+=val[x]*w[wcnt[x]];}
    inline void ers(int x){res-=val[x]*w[wcnt[x]]; wcnt[x]--;}
    inline void add(int x){vis[x]?ers(c[x]):ins(c[x]); vis[x]^=1;}
    inline void del(int x){vis[x]^=1; vis[x]?ins(c[x]):ers(c[x]);}
    inline void addq(int x)
    {
    	if(vis[cx[x]]) ers(c[cx[x]]);
    	c[cx[x]]=cy[x];
    	if(vis[cx[x]]) ins(c[cx[x]]);
    }
    inline void delq(int x)
    {
    	if(vis[cx[x]]) ers(c[cx[x]]);
    	c[cx[x]]=cp[x];
    	if(vis[cx[x]]) ins(c[cx[x]]);
    }
    int main()
    {
    	int n,k,m;
    	scanf("%d%d%d",&n,&k,&m);
    	for(int i=1;i<=k;i++) scanf("%lld",&val[i]);
    	for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
    	for(int i=1;i<n;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v),add(v,u);
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    	int tm=0;
    	for(int i=1;i<=m;i++)
    	{
    		int opt,l,r;
    		scanf("%d%d%d",&opt,&l,&r);
    		if(opt==1)
    		{
    			if(ld[l]>ld[r]) swap(l,r);
    			int lc=lca(l,r);
    			if(lc==l) q[++qtot]=(node){ld[l],ld[r],tm,i,0};
    			else q[++qtot]=(node){rd[l],ld[r],tm,i,lc};
    		}
    		else
    		{
    			++tm;
    			cx[tm]=l,cy[tm]=r;
    		}
    	}
    	for(int i=1;i<=tm;i++) cp[i]=c[cx[i]],c[cx[i]]=cy[i];
    	for(int i=tm;i>=1;i--) c[cx[i]]=cp[i];
    	const int B=pow(n,2.0/3)*0.5+1;
    	for(int i=1;i<=3*n;i++) bl[i]=i/B;
    	sort(q+1,q+qtot+1);
    	for(int i=1;i<=qtot;i++)
    	{
    		while(fr<q[i].r) add(qt[++fr]);
    		while(fr>q[i].r) del(qt[fr--]);
    		while(fl<q[i].l) del(qt[fl++]);
    		while(fl>q[i].l) add(qt[--fl]);
    		while(ft<q[i].t) addq(++ft);
    		while(ft>q[i].t) delq(ft--);
    		if(q[i].ex) add(q[i].ex);
    		ans[q[i].id]=res;
    		if(q[i].ex) del(q[i].ex);
    	}
    	for(int i=1;i<=m;i++)
    	if(ans[i]) printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    mysql分区表批量添加/删除range按天分区(int类型)
    mysql分区表批量添加/删除range按天分区(datetime类型)
    使用obd离线安装oceanbase
    mysqldump导出数据自增属性丢失案例
    Docker启动镜像并设置开机自启
    Docker启动mysql与elasticsearch以及nginx的命令
    Redis学习--从节点过期键清理策略
    Redis学习--渐进式rehash实现原理
    Redis学习--主节点过期键清理策略
    Redis学习--慢日志信息
  • 原文地址:https://www.cnblogs.com/Flying2018/p/13626738.html
Copyright © 2011-2022 走看看