【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())!='\n') 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-\text{顶上的点的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('\n');
		}
		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)\not=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\n",ans+num);
	}
	return 0;
}

P5590 赛车游戏

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

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

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

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

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

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

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

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

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

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

那么我们考虑化边权为点权,把每个点到它的路径总长作为点权\(val_i\),那么显然每个\(val_i\)的取值范围就是\([r_i,9\times 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",n,m),i=1;i<=m;++i) printf("%d %d %d\n",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",n,m),i=1;i<=m;++i) printf("%d %d %d\n",x[i],y[i],z[i]);
	return 0;
}

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

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


P5591 小猪佩奇学数学

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

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

\[Ans=\sum_{i=0}^n C_n^i\times p^i\times \lfloor \frac{i}{k} \rfloor \]

\[=\sum_{i=0}^n C_n^i\times p^i\times \frac{i-i\mod k}{k} \]

\[=\frac{1}{k}\times(\sum_{i=0}^n C_n^i\times p^i\times i-\sum_{i=0}^n C_n^i\times p^i\times(i\mod k)) \]

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

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

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

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

\[=np\times\sum_{i=0}^{n-1} C_{n-1}^i\times p^i=np\times(p+1)^{n-1} \]

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

\[\sum_{i=0}^n C_n^i\times p^i\times(i\mod k) \]

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

\[\sum_{i=0}^n C_n^i\times p^i\times(i\mod k) \]

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

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

\[\sum_{d=0}^{k-1} d\times(\sum_{i=0}^n C_n^i\times p^i\times \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\times(\omega_k^j\cdot p)^i \]

\[=\frac{1}{k}\sum_{j=0}^{k-1} (\omega_k^j\cdot p+1)^n\times\sum_{d=0}^{k-1} d\times(w_k^{k-j})^d \]

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

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

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

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

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

\[r\times\sum_{i=0}^{n-1} i\times r^i+r\times\sum_{i=0}^{n-1} r^i -n\times r^n \]

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

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

移项就得到\(S=\frac{n\times r^n}{r-1}-\frac{r^{n+1}-r}{(r-1)^2} (r\not=1)\)

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

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

综上所述这题就被解决了,而且由于\(998244352=2^{23}\times 119\),因此在\(k\in\{2^{\omega}|0\le\omega\le 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}}\rfloor\)分组

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

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

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

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

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

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

我们发现这个图的最大流也就是保留 \(a_i\operatorname{xor} b_j\ge 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",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",n-Trie.solve());
	return 0;
}

Postscript

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

posted @ 2019-10-13 21:18  空気力学の詩  阅读(275)  评论(2编辑  收藏  举报