• 【LGR-060】洛谷10月月赛 I div.1&div.2


    Preface

    一边打一边写作文打的像shit,T2失智严重特判错了233

    Orz Div1 Rank2的foreverlastnig聚聚,顺便说一句显然Luogu的比赛质量比以往显著提高了啊

    以下题目按难度顺序排序


    P5587 打字练习

    送分模拟题,注意行首退格的问题以及一个坑点:范文中也有退格

    #include<cstdio>
    #include<iostream>
    #include<string>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=10005;
    string a[N],b[N],tp; int ca,cb,cur,pre[N*10],t,lim; bool vis[N*10];
    inline void get_str(string& s)
    {
    	s=""; char ch; while ((ch=getchar())!='
    ') s+=ch;
    }
    inline void del(string& s)
    {
    	RI i; string t=s; s=""; lim=t.size();
    	for (i=0;i<lim;++i) pre[i]=i-1,vis[i]=1;
    	for (i=0;i<lim;++i) if (t[i]=='<')
    	{
    		vis[i]=0; pre[i+1]=pre[i];
    		if (~pre[i]) vis[pre[i]]=0,pre[i+1]=pre[pre[i]];
    	}
    	for (i=0;i<lim;++i) if (vis[i]) s+=t[i];
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	while (get_str(tp),tp!="EOF") a[++ca]=tp;
    	while (get_str(tp),tp!="EOF") b[++cb]=tp;
    	RI i,j; for (i=1;i<=ca;++i) del(a[i]);
    	for (i=1;i<=cb;++i) del(b[i]);
    	for (i=1;i<=min(ca,cb);++i)
    	for (j=0,lim=min(a[i].size(),b[i].size());j<lim;++j)
    	cur+=a[i][j]==b[i][j]; scanf("%d",&t);
    	return printf("%d",(int)(1.0*cur/(1.0*t/60)+0.5)),0;
    }
    

    P5588 小猪佩奇爬树

    分类讨论题。考虑我们对于每种颜色,如果有三个及以上的点的子树内是没有这种颜色的点的,那么显然这种颜色的答案就是(0)

    否则若有两个的话就是两端点的子树(size)乘积

    剩下的就是一些特殊的情况,比如所有点在从上而下的一条链上的,那么答案就是下面的点的(size)乘上(顶上的点的n- ext{顶上的点的size})

    还有只有一个点的以及没有点的都要单独讨论

    #include<cstdio>
    #include<cctype>
    #define int long long
    #define RI register int
    #define CI const int&
    #define Tp template <typename T>
    using namespace std;
    const int N=3000005;
    struct edge
    {
    	int to,nxt;
    }e[N<<1]; int n,head[N],cnt,col[N],x,y,pnum[N],all[N],fir[N],size[N],plc[N],f[N],g[N],ct[N];
    class FileInputOutput
    {
    	private:
    		static const int S=1<<21;
    		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
    		#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
    		char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[25];
    	public:
    		FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
    		Tp inline void read(T& x)
    		{
    			x=0; char ch; while (!isdigit(ch=tc()));
    			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
    		}
    		Tp inline void write(T x)
    		{
    			if (x<0) pc('-'),x=-x; RI ptop=0; while (pt[++ptop]=x%10,x/=10);
    			while (ptop) pc(pt[ptop--]+48); pc('
    ');
    		}
    		inline void flush(void)
    		{
    			fwrite(Fout,1,Ftop-Fout,stdout);
    		}
    		#undef tc
    		#undef pc
    }F;
    inline void addedge(CI x,CI y)
    {
    	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
    	e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
    }
    #define to e[i].to
    inline void DFS(CI now=1,CI fa=0)
    {
    	int tp=++ct[col[now]],fir=0,flag=0; size[now]=1;
    	for (RI i=head[now];i;i=e[i].nxt) if (to!=fa)
    	{
    		int ltp=ct[col[now]]; DFS(to,now); f[col[now]]+=size[now]*size[to];
    		size[now]+=size[to]; if (ltp!=ct[col[now]]) ++flag,fir=to;
    	}
    	f[col[now]]+=size[now]*(n-size[now]);
    	if (flag+(tp!=1||ct[col[now]]!=all[col[now]])==1)
    	{
    		++pnum[col[now]];
    		if (pnum[col[now]]==1) g[col[now]]=size[now]; else
    		if (pnum[col[now]]==2) g[col[now]]*=fir?(n-size[fir]):size[now];
    	}
    }
    #undef to
    signed main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i; for (F.read(n),i=1;i<=n;++i) F.read(col[i]),++all[col[i]];
    	for (i=1;i<n;++i) F.read(x),F.read(y),addedge(x,y);
    	for (DFS(),i=1;i<=n;++i)
    	{
    		if (!ct[i]) F.write(1LL*n*(n-1)/2LL); else if (ct[i]==1) F.write(f[i]);
    		else if (pnum[i]==2) F.write(g[i]); else F.write(0);
    	}
    	return F.flush(),0;
    }
    

    P5589 小猪佩奇玩游戏

    首先记(d(x))表示有多少个数在删除时会把(x)删掉,利用概率的可加性我们发现一个数的贡献就是(frac{1}{d(x)})

    那么撇开暴力计算的过程,我们发现很多数的(d(x)=1),那么这就意味着我们只要找出所有的(d(x) ot=1)的数计算贡献即可

    考虑直接暴力枚举(i),然后把(d(i^k)+1),用map存储答案即可

    最后跑出来发现(n)以内的数目很少,因此直接爆枚就可以了

    PS:这题有容斥的(log^2 n)的做法,我太菜了所以不会

    #include<cstdio>
    #include<map>
    #define RI register int
    #define CI const int&
    using namespace std;
    typedef map <int,int>:: iterator MI;
    const int N=1e9;
    int t,n,num; double ans; map <int,int> ct;
    int main()
    {
    	RI i; long long cur; for (i=2;i*i<=N;++i)
    	for (cur=i*i;cur<=N;cur*=i) ++ct[cur];
    	for (scanf("%d",&t);t;--t)
    	{
    		scanf("%d",&n); num=n; ans=0;
    		for (MI it=ct.begin();it!=ct.end();++it)
    		if (it->first<=n) --num,ans+=1.0/(it->second+1); else break;
    		printf("%.6lf
    ",ans+num);
    	}
    	return 0;
    }
    

    P5590 赛车游戏

    (以下偷懒直接贴了在Luogu写的题解并稍作修改)

    白天比赛的时候基本上都写出来了,结果判无用边的时候脑抽了一下挂了

    然而我还以为是后面写跪了一直在魔调233

    首先看到这题我们先日常考虑一些简单的情况,我们来分析一下:

    若起点不可达终点则输出(-1)(题面里之前没加上,可TM坑死人了)

    若存在一个点无法从起点到达或者是无法到达终点(比赛的时候脑抽写成了233)那么这个点就是无用的,可以把它删掉,那么所有与它相连的边都可以随便赋值

    然后考虑将剩下的边再建成图,那么此时出现环的话环上一定不合法,因此也可以判掉

    那么我们惊喜地发现现在剩下的图已经是个DAG了,并且从起点到每个点的所有路径长度都要相同,那我们按拓扑序逐步转移即可。接下来有两种做法:

    第一种是我比赛的时候写的,比较诡异,正确性感觉是对的但是又证明不来

    考虑先拓扑排序一遍,求出将边长视为(1)时从起点到每个点的路径长度区间范围([l_i,r_i])

    那么我们考虑化边权为点权,把每个点到它的路径总长作为点权(val_i),那么显然每个(val_i)的取值范围就是([r_i,9 imes l_i])

    考虑(1)号点的点权可以定下为(0),那么对于接下来的每个点,如果它的前驱点(j)的点权为(val_j),那么它的取值区间应该对([val_j+1,val_j+9])取交

    那么我们得出每个点的取值区间后直接在里面随便取一个值即可(顺手取最小值),同时再判掉一些无解的情况

    乍一看随便取可能会错,但是这里的后面的点权范围是在前面的路径情况下考虑过的结果,因此可以通过此题

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #define RI register int
    #define CI const int&
    using namespace std;
    typedef vector <int>:: iterator VI;
    const int N=2005,INF=1e9;
    struct interval
    {
    	int l,r;
    	inline interval(CI L=-INF,CI R=INF)
    	{
    		l=L; r=R;
    	}
    	friend inline interval operator & (const interval& A,const interval& B)
    	{
    		return interval(max(A.l,B.l),min(A.r,B.r));
    	}
    	inline void operator &=(const interval& ots)
    	{
    		*this=*this&ots;
    	}
    }v[N]; int n,m,x[N],y[N],z[N],q[N],val[N];
    bool f1[N],f2[N]; vector <int> pre[N];
    struct Graph
    {
    	struct edge
    	{
    		int to,nxt;
    	}e[N]; int head[N],cnt,deg[N];
    	inline void clear(void)
    	{
    		memset(head,0,n+1<<2); memset(deg,0,n+1<<2); cnt=0;
    	}
    	inline void addedge(CI x,CI y)
    	{
    		e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[y];
    	}
    	#define to e[i].to
    	inline void BFS(CI st,bool *vis)
    	{
    		RI H=0,T=1; vis[q[1]=st]=1; while (H<T)
    		{
    			int now=q[++H]; for (RI i=head[now];i;i=e[i].nxt)
    			if (!vis[to]) vis[to]=1,q[++T]=to;
    		}
    	}
    	inline bool Top_Sort(void)
    	{
    		RI H=0,T=0,i; for (i=1;i<=n;++i) if (!deg[i]) q[++T]=i;
    		while (H<T)
    		{
    			int now=q[++H]; for (i=head[now];i;i=e[i].nxt)
    			if (pre[to].push_back(now),!--deg[to]) q[++T]=to;
    		}
    		return T==n;
    	}
    	#undef to
    }A,B;
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
    	scanf("%d%d",&x[i],&y[i]),A.addedge(x[i],y[i]),B.addedge(y[i],x[i]);
    	for (A.BFS(1,f1),B.BFS(n,f2),i=1;i<=m;++i) if (!f1[x[i]]||!f2[x[i]]) z[i]=1;
    	if (!f1[n]) return puts("-1"),0;
    	for (A.clear(),i=1;i<=m;++i) if (!z[i]) A.addedge(x[i],y[i]);
    	if (!A.Top_Sort()) return puts("-1"),0; for (v[1]=interval(0,0),i=2;i<=n;++i)
    	{
    		int mi=INF,mx=-INF; for (VI it=pre[q[i]].begin();it!=pre[q[i]].end();++it)
    		mi=min(mi,v[*it].l+1),mx=max(mx,v[*it].r+1); v[q[i]]=interval(mi,mx);
    	}
    	for (i=1;i<=n;++i) if (v[i]=interval(v[i].r,9*v[i].l),v[i].l>v[i].r)
    	return puts("-1"),0; for (i=2;i<=n;++i)
    	{
    		interval tp; for (VI it=pre[q[i]].begin();it!=pre[q[i]].end();++it)
    		tp&=interval(val[*it]+1,val[*it]+9); v[q[i]]&=tp;
    		if (v[q[i]].l>v[q[i]].r) return puts("-1"),0; val[q[i]]=v[q[i]].l;
    	}
    	for (i=1;i<=m;++i) if (!z[i]) z[i]=val[y[i]]-val[x[i]];
    	for (printf("%d %d
    ",n,m),i=1;i<=m;++i) printf("%d %d %d
    ",x[i],y[i],z[i]);
    	return 0;
    }
    

    当然还有另一种更简单正确性也有保证的做法,我们考虑直接顺推每个点的权值区间,那么此时这个点的取法就会影响到后面了,因此我们可以倒着再做一遍,这样就可以保证正确性

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #define RI register int
    #define CI const int&
    using namespace std;
    typedef vector <int>:: iterator VI;
    const int N=2005,INF=1e9;
    struct interval
    {
    	int l,r;
    	inline interval(CI L=-INF,CI R=INF)
    	{
    		l=L; r=R;
    	}
    	friend inline interval operator & (const interval& A,const interval& B)
    	{
    		return interval(max(A.l,B.l),min(A.r,B.r));
    	}
    	inline void operator &=(const interval& ots)
    	{
    		*this=*this&ots;
    	}
    }v[N]; int n,m,x[N],y[N],z[N],q[N],val[N];
    bool f1[N],f2[N]; vector <int> pre[N];
    struct Graph
    {
    	struct edge
    	{
    		int to,nxt;
    	}e[N]; int head[N],cnt,deg[N];
    	inline void clear(void)
    	{
    		memset(head,0,n+1<<2); memset(deg,0,n+1<<2); cnt=0;
    	}
    	inline void addedge(CI x,CI y)
    	{
    		e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[y];
    	}
    	#define to e[i].to
    	inline void BFS(CI st,bool *vis)
    	{
    		RI H=0,T=1; vis[q[1]=st]=1; while (H<T)
    		{
    			int now=q[++H]; for (RI i=head[now];i;i=e[i].nxt)
    			if (!vis[to]) vis[to]=1,q[++T]=to;
    		}
    	}
    	inline bool Top_Sort(void)
    	{
    		RI H=0,T=0,i; for (i=1;i<=n;++i) if (!deg[i]) q[++T]=i;
    		while (H<T)
    		{
    			int now=q[++H]; for (i=head[now];i;i=e[i].nxt)
    			if (pre[to].push_back(now),!--deg[to]) q[++T]=to;
    		}
    		return T==n;
    	}
    	#undef to
    }A,B;
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
    	scanf("%d%d",&x[i],&y[i]),A.addedge(x[i],y[i]),B.addedge(y[i],x[i]);
    	for (A.BFS(1,f1),B.BFS(n,f2),i=1;i<=m;++i) if (!f1[x[i]]||!f2[x[i]]) z[i]=1;
    	if (!f1[n]) return puts("-1"),0;
    	for (A.clear(),i=1;i<=m;++i) if (!z[i]) A.addedge(x[i],y[i]);
    	if (!A.Top_Sort()) return puts("-1"),0; v[1]=interval(0,0);
    	for (i=2;i<=n;++i) for (VI it=pre[q[i]].begin();it!=pre[q[i]].end();++it)
    	v[q[i]]&=interval(v[*it].l+1,v[*it].r+9);
    	for (i=n;i>1;--i) for (VI it=pre[q[i]].begin();it!=pre[q[i]].end();++it)
    	v[*it]&=interval(v[q[i]].l-9,v[q[i]].r-1);
    	for (i=1;i<=n;++i) if (v[i].l>v[i].r) return puts("-1"),0;
    	for (i=1;i<=m;++i) if (!z[i]) z[i]=v[y[i]].l-v[x[i]].l;
    	for (printf("%d %d
    ",n,m),i=1;i<=m;++i) printf("%d %d %d
    ",x[i],y[i],z[i]);
    	return 0;
    }
    

    PS1:LTL的做法(即第二种做法)被叉掉了233

    PS2:这题正解好像是差分约束,我想了想还挺有道理


    P5591 小猪佩奇学数学

    因为被没带草稿本那粉笔在机房写了两黑板233

    来吧让我们来推式子QAQ,首先第一步考虑把下取整拆了:

    [Ans=sum_{i=0}^n C_n^i imes p^i imes lfloor frac{i}{k} floor ]

    [=sum_{i=0}^n C_n^i imes p^i imes frac{i-imod k}{k} ]

    [=frac{1}{k} imes(sum_{i=0}^n C_n^i imes p^i imes i-sum_{i=0}^n C_n^i imes p^i imes(imod k)) ]

    考虑对于括号内的式子前后分别计算,首先是

    [sum_{i=0}^n C_n^i imes p^i imes i ]

    有组合数有幂次考虑怎么化成二项式定理的形式,我们考虑用组合数吸收掉(i)

    [sum_{i=0}^n C_n^i imes p^i imes i=sum_{i=1}^n n imes C_{n-1}^{i-1} imes p^{i} ]

    [=np imessum_{i=0}^{n-1} C_{n-1}^i imes p^i=np imes(p+1)^{n-1} ]

    好了上面是热身,然后考虑怎么搞后面那部分:

    [sum_{i=0}^n C_n^i imes p^i imes(imod k) ]

    (imod k)显然很麻烦,我们考虑枚举(d=imod k),那么有((i-d)mod k=0),代进去有:

    [sum_{i=0}^n C_n^i imes p^i imes(imod k) ]

    [=sum_{d=0}^{k-1} d imessum_{i=0}^n C_n^i imes p^i[(i-d)mod k=0] ]

    然后下一步就要用到单位根反演了,不会的可以看浅谈单位根反演,代进去有:

    [sum_{d=0}^{k-1} d imes(sum_{i=0}^n C_n^i imes p^i imes frac{1}{k}sum_{j=0}^{k-1}frac{omega_k^{ij}}{omega_k^{dj}}) ]

    [=frac{1}{k}sum_{j=0}^{k-1}sum_{d=0}^{k-1} frac{d}{w_k^{dj}}sum_{i=0}^n C_n^i imes(omega_k^jcdot p)^i ]

    [=frac{1}{k}sum_{j=0}^{k-1} (omega_k^jcdot p+1)^n imessum_{d=0}^{k-1} d imes(w_k^{k-j})^d ]

    发现后面那个(sum_{d=0}^{k-1} d imes(w_k^{k-j})^d)是做这题的关键,考虑怎么快速计算一个一般形式的问题:

    [S=sum_{i=0}^{n-1} i imes r^i ]

    方法其实挺多的,这里我用的是扰动法(不知道的可以到 《具体数学》 上看下):

    [S=sum_{i=0}^{n-1} i imes r^i ]

    [=sum_{i=0}^{n-1} (i+1) imes r^{i+1} -n imes r^n ]

    [r imessum_{i=0}^{n-1} i imes r^i+r imessum_{i=0}^{n-1} r^i -n imes r^n ]

    前面那一项我们发现(S)又被我们凑出来了,而后面那一项可以用等比数列求和得到通式,则:

    [S=r imes S+r imes frac{r^n-1}{1-r}-n imes r^n ]

    移项就得到(S=frac{n imes r^n}{r-1}-frac{r^{n+1}-r}{(r-1)^2} (r ot=1))

    然后想必大家也发现了这里没有考虑(r=1)的情况,而显然有:

    [S=frac{n imes(n-1)}{2} (r=1) ]

    综上所述这题就被解决了,而且由于(998244352=2^{23} imes 119),因此在(kin{2^{omega}|0leomegale 20})时是存在单位根的

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=1<<20|5,mod=998244353;
    int n,p,k,g,w[N],ans,ret;
    inline int quick_pow(int x,int p=mod-2,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    inline void dec(int& x,CI y)
    {
    	if ((x-=y)<0) x+=mod;
    }
    inline int sub(CI x,CI y)
    {
    	int t=x-y; return t<0?t+mod:t;
    }
    inline int S(CI r,CI n)
    {
    	if (r==1) return 1LL*n*(n-1)%mod*quick_pow(2)%mod;
    	return sub(1LL*n*quick_pow(r,n)%mod*quick_pow(r-1)%mod,1LL*sub(quick_pow(r,n+1),r)*quick_pow(r-1,mod-3)%mod);
    }
    int main()
    {
    	scanf("%d%d%d",&n,&p,&k); g=quick_pow(3,(mod-1)/k);
    	RI i; for (w[0]=i=1;i<=k;++i) w[i]=1LL*w[i-1]*g%mod;
    	for (ans=1LL*n*p%mod*quick_pow(p+1,n-1)%mod,i=0;i<k;++i)
    	inc(ret,1LL*quick_pow((1LL*w[i]*p%mod+1)%mod,n)*S(w[k-i],k)%mod);
    	dec(ans,1LL*quick_pow(k)*ret%mod);
    	return printf("%d",1LL*quick_pow(k)*ans%mod),0;
    }
    

    P5592 美德的讲坛

    我太菜了,以下解法出自官方题解

    首先考虑找出一个(omega),满足(2^{omega}le x<2^{omega+1}),然后我们把(a_i)按照(lfloor frac{a_i}{2^{omega}} floor)分组

    那么首先我们发现每个组内部的两两的异或都是(<x)

    然后细细分析我们发现只有相邻的组之前才有可能产生(<x)的异或值,因此我们把这两组的点拿出来,问题变成:

    现在有两组点,左边每个点有一个权值 (a_i),右边每个点有一个权值(b_i)。现在要在左右各选出一些点,使得两两异或和(<x)

    考虑用最小割来解决,如果我们将源点向左边的点连流量(1)的边,右边的点向汇点连流量(1)的边,然后当(a_ioperatorname{xor} b_jge x)时,(i)(j)连流量(infty)的边

    那么跑出这个图的最小割,我们发现这个割就是要删去一些数字与相应源汇点的边,然后使得剩下的数字两两异或都(<x)的方案

    然后考虑从最小割等于最大流的角度入手,由于这里是匹配问题我们用模拟费用流的思想,同时由于涉及异或我们建立0/1Trie

    我们发现这个图的最大流也就是保留 (a_ioperatorname{xor} b_jge x)的边时,该二分图的最大匹配。

    考虑在Trie树上统计,令(solve(a,b,dep))表示当Trie树上以(x,y)为根的子树之间进行匹配,两棵子树的最大深度均为(dep)

    然后记(a_0,a_1,b_0,b_1)表示(a/b)的左/右子树,用(|a|)表示(a)的子树里的点数

    我们分类讨论,当(x)(dep)这一位为(1)时,就意味着只有(a_0)(b_1)(a_1)(b_0)可以匹配

    此时答案就是(solve(a_0,b_1,dep-1)+solve(a_1,b_0,dep-1))

    (x)(dep)这一位为(0)时,那么(a_0)(b_1)(a_1)(b_0)一定可以匹配

    • (|a_0|<|b_1|)(|a_1|<|b_0|)时,答案就是(|a|)
    • (|a_0|>|b_1|)(|a_1|>|b_0|)时,答案就是(|b|)
    • (|a_0|<|b_1|)(|a_1|>|b_0|)时,答案就是(min(solve(a_1,b_1,dep-1),|b_1|-|a_0|,|a_1|-|b_0|)+|a_0|+|b_0|)
    • (|a_0|>|b_1|)(|a_1|<|b_0|)时,答案就是(min(solve(a_0,b_0,dep-1),|b_0|-|a_1|,|a_0|-|b_1|)+|a_1|+|b_1|)

    然后注意讨论下(a=b)的情况以及(x=0)的情况即可

    最后修改由于每次最多只有(log)的节点被改动了,因此类似于记忆化搜索来解决

    总复杂度((n+q)log^2 max(a_i)),足以通过此题

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    #define CL const LL&
    using namespace std;
    typedef long long LL;
    const int N=100005,R=60;
    int n,q,x,rt; LL a[N],s,y;
    class Zero_One_Trie
    {
    	private:
    		struct segment
    		{
    			int ch[2],size,ans;
    		}node[N*R<<2]; bool vis[N*R<<2]; int tot;
    	public:
    		inline Zero_One_Trie(void) { rt=tot=1; }
    		#define lc(x) node[x].ch[0]
    		#define rc(x) node[x].ch[1]
    		#define S(x) node[x].size
    		#define A(x) node[x].ans
    		inline void insert(int& now,CL val,CI dep=R-1)
    		{
    			if (!~dep) return; if (!now) now=++tot; vis[now]=0; ++S(now);
    			insert(node[now].ch[(val>>dep)&1LL],val,dep-1);
    		}
    		inline void remove(int& now,CL val,CI dep=R-1)
    		{
    			if (!~dep) return; vis[now]=0; --S(now);
    			remove(node[now].ch[(val>>dep)&1LL],val,dep-1);
    		}
    		inline int solve(CI x=rt,CI y=rt,CI dep=R-1)
    		{
    			if (!x||!y||!S(x)||!S(y)) return 0; if (!~dep) return x==y?(S(x)-(S(x)&1)):min(S(x),S(y));
    			if (vis[x]&&vis[y]) return A(x); vis[x]=vis[y]=1; int ret;
    			if ((s>>dep)&1LL)
    			{
    				if (x==y) ret=solve(lc(x),rc(x),dep-1);
    				else ret=solve(lc(x),rc(y),dep-1)+solve(rc(x),lc(y),dep-1);
    			} else
    			{
    				int ax=solve(lc(x),lc(y),dep-1),ay=solve(rc(x),rc(y),dep-1);
    				if (x==y) ret=min(S(lc(x))-ax,S(rc(x))-ay)+ax+ay; else
    				{
    					if ((S(lc(x))<=S(rc(y)))==(S(rc(x))<=S(lc(y)))) ret=min(S(x),S(y)); else
    					if (S(lc(x))<S(rc(y))) ret=min(ay,min(S(rc(y))-S(lc(x)),S(rc(x))-S(lc(y))))+S(lc(x))+S(lc(y));
    					else ret=min(ax,min(S(lc(y))-S(rc(x)),S(lc(x))-S(rc(y))))+S(rc(x))+S(rc(y));
    				}
    			}
    			return A(x)=A(y)=ret;
    		}
    		#undef lc
    		#undef rc
    		#undef S
    		#undef A
    }Trie;
    int main()
    {
    	RI i; for (scanf("%d%d%lld",&n,&q,&s),i=1;i<=n;++i)
    	scanf("%lld",&a[i]),Trie.insert(rt,a[i]);
    	if (!s) { for (RI i=1;i<=q+1;++i) puts("1"); return 0; }
    	for (printf("%d
    ",n-Trie.solve()),i=1;i<=q;++i)
    	scanf("%d%lld",&x,&y),Trie.remove(rt,a[x]),
    	Trie.insert(rt,a[x]=y),printf("%d
    ",n-Trie.solve());
    	return 0;
    }
    

    Postscript

    我发现现在我对一切都一无所知

  • 相关阅读:
    Git的环境搭建
    AmazeUI HTML元素
    AmazeUI布局
    AmazeUI基本样式
    Bash简介
    Linux下拷贝目录和删除
    linux下的定时任务
    缓存
    隔离
    DEDECMS使用SQL命令批量替换语句
  • 原文地址:https://www.cnblogs.com/cjjsb/p/11668410.html
Copyright © 2020-2023  润新知