首页 > 其他分享 >P4689 [Ynoi2016] 这是我自己的发明 与 P5268 [SNOI2017] 一个简单的询问0

P4689 [Ynoi2016] 这是我自己的发明 与 P5268 [SNOI2017] 一个简单的询问0

时间:2024-08-18 16:16:17浏览次数:4  
标签:rt siz ll Ynoi2016 dfn SNOI2017 ans P4689 define

思路:

首先可以先考虑没有换根的情况。

先将树拍到 dfn 序上,那么一个子树 \(u\) 的所有点的 dfn 序区间为 \([dfn_u,dfn_u+siz_u-1]\)。

那么询问变为:

  • 每次给定两个区间 \([l_1,r_1],[l_2,r_2]\),对于在第一个区间内的点 \(x\) 和在第二个区间的点 \(y\),若 \((x,y)\) 有贡献,当且仅当 \(w_x=w_y\)。

  • 询问有贡献的点对数量。

P5268 [SNOI2017] 一个简单的询问

设 \(F(l_1,r_1,l_2,r_2)\) 表示 \([l_1,r_1]\) 与 \([l_2,r_2]\) 的贡献,那么:

\[F(l_1,r_1,l_2,r_2) = F(1,r_1,1,r_2) - F(1,l_1-1,1,r_2) - F(1,r_1,1,l_2-1) - F(1,l_1-1,1,l_2-1) \]

那么一个询问就都转化为了四个 \(F(1,x,1,y)\) 的形式,考虑如何求 \(F(1,x,1,y)\),先钦定 \(x \le y\),那么考虑莫队:

  • 设当前 \(p_{1,x},p_{2,x}\) 分别表示两个区间 \(x\) 的出现次数。

  • 若 \(x \gets x+1\) 时,贡献会增加 \(p_{2,a_{x+1}}\)。

  • 若 \(x \gets x-1\) 时,贡献会减少 \(p_{2,a_x}\)。

  • 若 \(y \gets y+1\) 时,贡献会增加 \(p_{1,a_{y+1}}\)。

  • 若 \(y \gets y-1\) 时,贡献会减少 \(p_{1,a_y}\)。

现在再考虑换根操作,若当前以 \(rt\) 为根:

  • 若 \(rt\) 不在初始以 \(1\) 为根时 \(x\) 的子树内,则不好造成影响。

  • 否则 \(x\) 子树内的点即为除了\((x \to rt)\) 路径上最接近 \(x\) 的点 \(y\) 子树内的点的全部点。

因为 \(x\) 在原始树上始终是 \(rt\) 的父亲,则 \(y\) 是 \(rt\) 的 \(dep_{rt}-dep_{x}-1\) 级祖先,直接倍增即可。

时间复杂度为 \(O(N\sqrt{M}+M \log N+M)\)。

完整代码:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=1e5+10,M=4e6+10,K=17;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll op,n,m,t,q,u,v,rt,sum,l1,r1,l2,r2,l,r,cnt;
ll A[N],a[N],b[N],w[N],d[N],siz[N],dfn[N],p1[N],p2[N],ans[M];
ll F[N][K];
vector<pi> X,Y;
vector<ll> E[N];
struct Ques{
    ll x,y;
    ll id;
    ll v;
    inline bool operator<(const Ques &rhs)const{
        if(A[x]^A[rhs.x])
          return A[x]<A[rhs.x];
        return y>rhs.y;
    }
}Q[M];
inline void add(ll u,ll v){
    E[u].push_back(v);
    E[v].push_back(u);
}
inline void dfs(ll u,ll fa){
    For(i,1,K-1)
      F[u][i]=F[F[u][i-1]][i-1];
    dfn[u]=++cnt;
    w[cnt]=a[u];
    siz[u]=1;
    for(auto v:E[u]){
        if(v==fa)
          continue;
        F[v][0]=u;
        d[v]=d[u]+1;
        dfs(v,u);
        siz[u]+=siz[v];
    }
}
inline ll get_fa(ll u,ll k){
    _For(i,0,K-1){
        if((k>>i)&1ll){
            k-=(1ll<<i);
            u=F[u][i];
        }
    }
    return u;
}
inline vector<pi> get(ll x){
    vector<pi> ans;
    if(x==rt)
      ans.push_back({1,n});
    else if(dfn[x]<=dfn[rt]&&dfn[rt]<=dfn[x]+siz[x]-1){
        ll y=get_fa(rt,d[rt]-d[x]-1);
        if(dfn[y]!=1)
          ans.push_back({1,dfn[y]-1});
        if(dfn[y]+siz[y]<=n)
          ans.push_back({dfn[y]+siz[y],n});
    }
    else
      ans.push_back({dfn[x],dfn[x]+siz[x]-1});
    return ans;
}
inline void get(ll l1,ll r1,ll l2,ll r2){
    Q[++q]={r1,r2,cnt,1};
    if(l1-1)
      Q[++q]={l1-1,r2,cnt,-1};
    if(l2-1)
      Q[++q]={r1,l2-1,cnt,-1};
    if(l1-1&&l2-1)
      Q[++q]={l1-1,l2-1,cnt,1};
}
inline void insert1(ll x){
    sum+=p2[w[x]];
    p1[w[x]]++;
}
inline void insert2(ll x){
    sum+=p1[w[x]];
    p2[w[x]]++; 
}
inline void del1(ll x){
    sum-=p2[w[x]];
    p1[w[x]]--;
}
inline void del2(ll x){
    sum-=p1[w[x]];
    p2[w[x]]--;
}
bool End;
int main(){
    n=read(),m=read();
    For(i,1,n){
        a[i]=read();
        b[++cnt]=a[i];
    }
    sort(b+1,b+cnt+1);
    cnt=unique(b+1,b+cnt+1)-(b+1);
    For(i,1,n)
      a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
    cnt=0;
    For(i,1,n-1){
        u=read(),v=read();
        add(u,v);
    }
    dfs(1,1);
    cnt=0;
    For(i,1,m){
        op=read(),u=read();
        if(op==1){
            rt=u;
            continue;
        }
        ++cnt;
        v=read();
        X=get(u);
        Y=get(v);
        for(auto x:X)
          for(auto y:Y)
            get(x.fi,x.se,y.fi,y.se);
    }
    t=max(n/max((ll)sqrt(m),1ll),1ll);
    For(i,1,n)
      A[i]=(i-1)/t+1;
    For(i,1,q)
      if(Q[i].x>Q[i].y)
        swap(Q[i].x,Q[i].y);
    sort(Q+1,Q+q+1);
    For(i,1,q){
        while(l<Q[i].x)
          insert1(++l);
        while(l>Q[i].x)
          del1(l--);
        while(r<Q[i].y)
          insert2(++r);
        while(r>Q[i].y)
          del2(r--);
        ans[Q[i].id]+=sum*Q[i].v;
    }
    For(i,1,cnt){
        write(ans[i]);
        putchar('\n');
    }
	//cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}

标签:rt,siz,ll,Ynoi2016,dfn,SNOI2017,ans,P4689,define
From: https://www.cnblogs.com/rgw2010/p/18364621

相关文章

  • [Ynoi2016] 镜中的昆虫 题解
    难度在最近遇到的题里相对较高,在这里写一篇珂学题解。(以下是学校给的部分分)\(20\%\):直接暴力枚举。另外\(20\%\):假如我们取\(pre\),对于\(pre<l\)的,\(ans++\),明显二维偏序,树状数组或\(cdq\)即可,时间复杂度\(O(n\logn)\)。另外\(40\%\):相当于多加一个时间维,三维偏序,\(......
  • P4690 Ynoi2016 镜中的昆虫
    P4690Ynoi2016镜中的昆虫原题不会见祖宗。前置珂朵莉树、cdq分治、树状数组思路单点修改区间查询定义\(pre_i\)表示\(col_i\)的前一个一样颜色的位置,那么对于一段区间查询\([l,r]\),我们只需要查询有区间内有多少个\(pre_i<l\)。每次修改时就相当于修改四个同颜色......
  • P4688 Ynoi2016 掉进兔子洞
    P4688Ynoi2016掉进兔子洞经典莫队加bitset。思路不难发现最终答案就是:\[(r_1-l_1+1)+(r_2-l_2+1)+(r_3-l_3+1)-3\timessize\]其中\(size\)表示3个区间内出现了多少个公共元素。看到这么多区间,不妨有把区间拆下来搞莫队的想法。先不考虑询问个数的限制,我们考虑使用......
  • C117 莫队配合 bitset P4688 [Ynoi2016] 掉进兔子洞
    视频链接:C117莫队配合bitsetP4688[Ynoi2016]掉进兔子洞_哔哩哔哩_bilibili   LuoguP4688[Ynoi2016]掉进兔子洞//莫队配合bitsetO(n*sqrt(n))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<bitset>usin......
  • P4690 [Ynoi2016] 镜中的昆虫
    P4690[Ynoi2016]镜中的昆虫本质就是区间修改,区间数颜色弱化一下,先考虑不带修的情况,也就是P1972[SDOI2009]HH的项链其难点在于区间颜色的去重,需要想一个办法让区间内一个颜色只被数一次我们可以去维护一个\(Nxt\)数组,表示下一个与当前位置颜色相同的位置若当前位......
  • P4690 [Ynoi2016] 镜中的昆虫 题解
    题目链接:镜中的昆虫经典题了,我们首先回顾下颜色数的常见做法统计:对每个位置维护一个\(pre_i\),表示与当前位置相同的颜色上一次出现位置。那么我们分讨一下。查询\([l,r]\)得到颜色数,对于\(pre_i<l\)的\(i\)点,显然它就是这个区间内\(a_i\)对应颜色出现的第一个位置,我们......
  • P4688 [Ynoi2016] 掉进兔子洞
    题意给定长度为\(n\)的序列\(s\)。有\(m\)个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立。Sol不难发现答案即为求:\(r1-l1+r2-l2+r3-l3+3-siz\)。其中\(siz\)表示三个区间的公共颜色的个数。仔细......
  • P5365 SNOI2017 英雄联盟
    P5365SNOI2017英雄联盟基本思路刚洗完澡做的,脑子转不动了。疑似开始自动化思考了,状态转移方程是这一坨\(F[i][j]*=F[i-1][j-k*w[i]]\)事实上根本不对。首先当前的方案数完全没有体现出来,只乘了之前的方案数,而且这是一个最优性问题,不是计数问题,要在两种状况中做出选......
  • [Ynoi2016] 镜中的昆虫
    64MB,1.5s题目描述您正在欣赏galgame的HS,然后游戏崩溃了,于是您只能做数据结构题了:维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。将区间 \([l,r]\) 的值修改为 \(x\)。询问区间 \([l,r]\) 出现了多少种不同的数,也就是说同一个数出现多次只算一个。......
  • P5268 [SNOI2017] 一个简单的询问
    一个简单的询问显然这个询问并不简单如果做过莫比乌斯反演入门题problemb就会想到利用容斥将询问拆成四个那么我们现在的问题变成如何求[1,l][1,r]两个区间之间的答案,那么也是直接用莫队即可,只是维护的是两个区间的右端点,和原来的莫队有一些不一样,但是大体相同。#include<......