zoukankan      html  css  js  c++  java
  • 【洛谷4689】[Ynoi2016] 这是我自己的发明(莫队)

    点此看题面

    • 给定一棵(n)个点的有根树,每个点上有一个数。
    • 两种操作,给这棵树换一个根,或是询问有多少种方式从给定两点的子树选出两个相同的数。
    • (nle10^5,qle5 imes10^5)

    简单莫队

    首先不难想到要把子树对应到(dfs)序列上的一个区间。

    而对于序列上的这种问题实际上是有原题的:【洛谷5268】[SNOI2017] 一个简单的询问

    转化(dfs)

    经典套路。

    如果根不在当前点(x)子树内,那么对应的区间就是在原树上对应的(dfs)序列区间。

    否则,如果根在当前点(x)子树内,我们从根向上倍增找到它是在(x)的哪个子节点(设其为(y))的子树中,那么对应的区间就应该是整个序列抠出(y)对应的(dfs)序列区间后剩余的一段前缀和一段后缀。

    考虑我们将序列复制一遍,那么这种情况也可以表示成一个完整区间了。

    然后就结束了。

    代码:(O(qsqrt n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define M 500000 
    #define LN 20
    #define LL long long
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,m,Qt,a[N+5],dv[N+5],sz,bl[2*N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    struct Q
    {
    	int op,p,a,b;I Q(CI t=0,CI i=0,CI x=0,CI y=0):op(t),p(i),a(x),b(y){}
    	I bool operator < (Con Q& o) Con {return bl[a]^bl[o.a]?a<o.a:(bl[a]&1?b<o.b:b>o.b);}
    }q[4*M+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    int d,b[2*N+5],dep[N+5],dI[N+5],dO[N+5],f[N+5][LN+5];I void dfs(CI x,CI lst=0)//dfs预处理
    {
    	RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];//预处理倍增数组
    	for(dI[x]=++d,b[d]=b[n+d]=a[x],i=lnk[x];i;i=e[i].nxt)
    		e[i].to^lst&&(dep[e[i].to]=dep[f[e[i].to][0]=x]+1,dfs(e[i].to,x),0);dO[x]=d;
    }
    I int Jump(RI x,RI d) {for(RI i=0;i<=LN;++i) (d>>i)&1&&(x=f[x][i]);return x;}//倍增上跳找所在子节点
    int c1[N+5],c2[N+5];LL ans[M+5];I void Mo()//莫队
    {
    	RI i,A=0,B=0;LL t=0;for(sz=sqrt(2*n),i=1;i<=2*n;++i) bl[i]=(i-1)/sz+1;
    	for(sort(q+1,q+Qt+1),i=1;i<=Qt;ans[q[i].p]+=q[i].op*t,++i)
    	{
    		W(A<q[i].a) ++c1[b[++A]],t+=c2[b[A]];W(A>q[i].a) --c1[b[A]],t-=c2[b[A--]];
    		W(B<q[i].b) ++c2[b[++B]],t+=c1[b[B]];W(B>q[i].b) --c2[b[B]],t-=c1[b[B--]];
    	}for(i=1;i<=m;++i) ~ans[i]&&(writeln(ans[i]),0);
    }
    int rt;I void Get(CI x,int& l,int& r)//求区间
    {
    	if(x==rt) return (void)(l=1,r=n);if(dI[rt]<dI[x]||dI[rt]>dO[x]) return (void)(l=dI[x],r=dO[x]);//如果就是根;如果根节点不在子树内
    	RI y=Jump(rt,dep[rt]-dep[x]-1);l=dO[y]+1,r=n+dI[y]-1;//找到对应儿子,就是扣去其子树的剩余部分
    }
    int main()
    {
    	RI i;for(read(n,m),i=1;i<=n;++i) read(a[i]),dv[i]=a[i];
    	for(sort(dv+1,dv+n+1),i=1;i<=n;++i) a[i]=lower_bound(dv+1,dv+n+1,a[i])-dv;//离散化
    	RI x,y;for(i=1;i^n;++i) read(x,y),add(x,y),add(y,x);dfs(1);
    	RI op,a,b,c,d;for(rt=i=1;i<=m;++i) switch(read(op),op)
    	{
    		case 1:read(x),rt=x,ans[i]=-1;break;//修改的ans标记为-1
    		case 2:read(x,y),Get(x,a,b),Get(y,c,d),q[++Qt]=Q(1,i,b,d),
    			q[++Qt]=Q(-1,i,b,c-1),q[++Qt]=Q(-1,i,a-1,d),q[++Qt]=Q(1,i,a-1,c-1);break;//容斥拆成四个区间
    	}return Mo(),clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    oracle函数 TO_DATE(X[,c2[,c3]])
    oracle函数 TO_CHAR(x[[,c2],C3])
    oracle函数 RAWTOHEX(c1)
    oracle HEXTORAW(c1)
    oracle函数 CONVERT(c1,set1,set2)
    oracle函数 ROWIDTOCHAR(rowid)
    oracle函数 chartorowid(c1)
    创建可按比例调整的布局的 Windows 窗体
    A Byte of Python(简明Python教程) for Python 3.0 下载
    使用异步事件在后台进行计算并报告进度
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4689.html
Copyright © 2011-2022 走看看