Pbri

Codeforces Round #736 (Div. 1) 题解

Codeforces Round #736 (Div. 1) 题解

A

如果一个点身边有比他大的点那么他最后一定死了,如果没有则一定不会死。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <set>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int n,m,q,ans,cnt[MAXN];
int main(){
	n=read(),m=read();
	FUP(i,1,m)
	{
		int u=read(),v=read();
		cnt[min(u,v)]++;
	}
	FUP(i,1,n) if(!cnt[i]) ans++;
	q=read();
	while(q--)
	{
		int opt=read();
		if(opt==1)
		{
			int u=read(),v=read();
			if(u>v) swap(u,v);
			if(!cnt[u]) ans--;
			cnt[u]++;
		}
		if(opt==2)
		{
			int u=read(),v=read();
			if(u>v) swap(u,v);
			if(cnt[u]==1) ans++;
			cnt[u]--;
		}
		if(opt==3) printf("%d\n",ans);
	}
	return 0;
}

B

考虑两个数 \(a,b(b>a)\) 是否存在解。设 \(a=pm+r,b=qm+r\) ,那么 \(b-a=(q-p)m\) ,也就是说如果两个数的差不是 \(1\) ,我们选取这个差的任意一个不是 \(1\) 的因子作为 \(m\) 就能构造出解。对于多个数,我们只需要让他们的 \(\gcd\) 不是 \(1\) 就能够构造出解。所以我们对于每个位置倍增的往后跳,最终能跳到的位置就是由自己开始能到的最远的位置,对所有的答案取 \(\max\) 即可。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
ll read()
{
	ll w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
ll getgcd(ll a,ll b){return b?getgcd(b,a%b):a;}
int T;
int n,ans;
ll a[MAXN],cf[MAXN],bz[MAXN][20];
void solve()
{
	ans=0;
	n=read();
	FUP(i,1,n) FUP(j,0,19) bz[i][j]=1;
	FUP(i,1,n) a[i]=read();
	FUP(i,1,n-1) cf[i]=abs(a[i]-a[i+1]);
	FDW(i,n-1,1)
	{
		bz[i][0]=cf[i];
		FUP(j,1,19)
		{
			if((1<<j)+i-1>n-1) break;
			bz[i][j]=getgcd(bz[i][j-1],bz[i+(1<<(j-1))][j-1]);
			if(bz[i][j]==1) break;
		}
		int re=0,p=i;
		ll tmp=0;
		FDW(j,19,0)
		{
			if(getgcd(bz[p][j],tmp)==1) continue;
			tmp=getgcd(tmp,bz[p][j]),re+=(1<<j),p+=(1<<j);
			//printf("p=%d j=%d tmp=%lld\n",p,j,tmp);
		}
		//puts("");
		ans=max(ans,re);
	}
	printf("%d\n",ans+1);
}
int main(){
	T=read();
	while(T--) solve();
	return 0;
}

C

考虑我们要求的答案实际上就是 \(\sum_{i=1}^n\dbinom{3i}{x}\) ,我们发现这很接近于对杨辉三角一列求和,然而只取了其中一部分,那么我们自然的把其他两项也考虑求一下,因为是对一列,所以我们把 \(i\) 的求和下标改成 \(0\)

\(f(x,y)=\sum_{i=0}^n\dbinom{3i+y}{x}(0\le y\le 2)\) ,那么根据组合数优秀的递推性质,我们可以将组合数拆开。得到

\[f(x,1)=\sum_{i=0}^n\dbinom{3i+1}{x}=\sum_{i=0}^n\dbinom{3i}{x}+\sum_{i=0}^n\dbinom{3i}{x-1}=f(x,0)+f(x-1,0) \]

\[f(x,2)=\sum_{i=0}^n\dbinom{3i+2}{x}=\sum_{i=0}^n\dbinom{3i+1}{x}+\sum_{i=0}^n\dbinom{3i+1}{x-1}=f(x,1)+f(x-1,1) \]

然后因为 \(f(x,0)+f(x,1)+f(x,2)\) 本质上是对第 \(x\) 列的前 \(3n+2\) 项求和,所以可以得到

\[f(x,0)+f(x,1)+f(x,2)=\dbinom{3n+3}{x} \]

然后你解一下方程就可以知道以下递推式( \(f(x,2)\) 没有用了,只是用来推导的)

\[\begin{cases} f(x,0)=\dfrac{\tbinom{3n+3}{x+1}-2f(x-1,0)-f(x-1,1)}{3}\\\\f(x,1)=f(x,0)+f(x-1,0)\end{cases} \]

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 1000010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
const ll inv3=333333336;
ll poww(ll a,int b)
{
	ll ans=1,base=a;
	while(b)
	{
		if(b&1) ans=ans*base%MOD;
		base=base*base%MOD;
		b>>=1;
	}
	return ans;
}
int n,q;
ll fac[MAXN*5],invfac[MAXN*5],f[MAXN*4][2];
ll C(int x,int y){return fac[x]*invfac[y]%MOD*invfac[x-y]%MOD;}
int main(){
	n=read(),q=read();
	fac[1]=invfac[1]=1;
	FUP(i,2,n*3+6) fac[i]=fac[i-1]*i%MOD;
	invfac[n*3+6]=poww(fac[n*3+6],MOD-2);
	FDW(i,n*3+5,2) invfac[i]=invfac[i+1]*(i+1)%MOD;
	//FUP(i,1,5*n) printf("i=%d fac=%lld invfac=%lld\n",i,fac[i],invfac[i]);
	f[0][0]=f[0][1]=n+1;
	//printf("f[1][0]=%lld f[1][1]=%lld\n",f[1][0],f[1][1]);
	FUP(i,1,3*n)
	{
		f[i][0]=((C(3*n+3,i+1)-2*f[i-1][0]%MOD+MOD)%MOD-f[i-1][1]+MOD)%MOD*inv3%MOD;
		f[i][1]=(f[i][0]+f[i-1][0])%MOD;
		//printf("C=%lld f[%d][0]=%lld f[%d][1]=%lld\n",C(3*n+3,i+1),i,f[i][0],i,f[i][1]);
	}
	FUP(i,1,q) printf("%lld\n",f[read()][0]);
	return 0;
}

D

考虑皮克定理:

\[S=a+\dfrac{b}{2}-1 \]

其中:\(S\) 是面积,\(b\) 是边界上的点,\(a\) 是内部的点。

以及面积公式:

\[S=\dfrac{|bc+de+fa-ad-be-cf|}{2} \]

其中三个点的坐标分别是 \((a,b),(c,d),(e,f)\)

以及求边上点的个数公式:

\[b=\gcd(|x_2-x_1|,|y_2-y_1|)+\gcd(|x_3-x_2|,|y_3-y_2|)+\gcd(|x_1-x_3|,|y_1-y_3|) \]

以及关于 \(\gcd\,mod4\) 的公式:

\[当a,b是偶数时 \gcd(a\mod4,b\mod4)=\gcd(a,b)\mod 4 \]

\(\LaTeX\)\(\mod4\) 的排版好鬼畜啊 )

因为只是看 \(a\) 是不是奇数,所以我们只需要其他两项除以二的结果,因为计算 \(S,\frac{b}{2}\) 都需要除以 \(2\) ,所以对于计算过程中的每一项我们需要他 \(mod4\) 的结果,但是求 \(\gcd\) 之前我们不能 \(mod4\) ,因为坐标不一定是偶数,所以我们需要设 \(cnt[i][x_0][y_0][g_0](0\le x_0,y_0,g_0\le 3)\) 表示满足 \(\begin{cases}x_j\equiv x_0\pmod4\\y_j\equiv y_0\pmod4\\\gcd(|x_j-x_i|,|y_j-y_i|)\equiv g_0\pmod4\end{cases}\)\(j\) 的数量。这样我们对每个 \(i\) 设他为一个端点,然后枚举另外两个端点中一个的这些数值,然后再枚举另外一个端点的这些数值,注意枚举的时候应该与第一个端点的这些值的奇偶性相同,因为如果不同的话,那么这两个点所对的边的横纵距离就不一定是偶数,那么计算 \(\gcd\) 就会有问题。先把面积不是整数的判出去。然后因为 \(S\) 是整数,那么 \(b\) 一定是偶数,因为两个端点到 \(i\) 的边的奇偶性相同,所以底下那条边的 \(\gcd\) 一定是偶数,然后我们就可以放心的用他们 \(\mod 4\) 的结果去计算了。然后如果三条边都是偶数,那么这个三角形会被计算六遍,如果只有一条边是偶数,那么因为我们保证了两个端点所对的边是偶数,那么 \(g_1,g_2\) 一定都是奇数,而这种三角形只会被计算 \(2\) 遍,我们根据 \(g_1\) 的奇偶性判断是哪种三角形就好了,最后除一除。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 6010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int Sum(int a,int b,int c,int d,int e,int f)
{
	int ret=b*c+d*e+a*f-a*d-b*e-c*f;
	return (ret%4+4)%4;
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int n,x[MAXN],y[MAXN],cnt[4][4][4];
ll re1,re2;
int main(){
	n=read();
	FUP(i,1,n) x[i]=read(),y[i]=read();
	FUP(i,1,n)
	{
		memset(cnt,0,sizeof(cnt));
		FUP(j,1,n)
		{
			if(i==j) continue;
			cnt[x[j]%4][y[j]%4][gcd(abs(x[j]-x[i]),abs(y[j]-y[i]))%4]++;
		}
		FUP(x1,0,3)
		{
			FUP(y1,0,3)
			{
				FUP(g1,0,3)
				{
					ll p1=cnt[x1][y1][g1];
					cnt[x1][y1][g1]--;
					for(int x2=(x1&1);x2<4;x2+=2)
					{
						for(int y2=(y1&1);y2<4;y2+=2)
						{
							for(int g2=(g1&1);g2<4;g2+=2)
							{
								ll p2=cnt[x2][y2][g2];
								int S=Sum(x[i],y[i],x1,y1,x2,y2);
								if(S&1) break;
								S>>=1;
								int g3=gcd(abs(x2-x1),abs(y2-y1));
								if((S-(g1+g2+g3)/2+1)&1)
								{
									if(g1&1) re1+=p1*p2;
									else re2+=p1*p2;
								}
							}
						}
					}
					cnt[x1][y1][g1]++;
				}
			}
		}
	}
	printf("%lld\n",re1/2+re2/6);
	return 0;
}

E

数连通块自然要有代表思想。如果两个位置在同一行或同一列,并且他们之间的数全部都 \(\le x\) ,那么我们称这两个点是直达的。如果一个点是所有它一次直达的点中最小的点(如果存在相同我们可以按照从上到下从左到右的顺序规定大小)我们就称这个点是一个代表。接下来我们证明一个结论:一个连通块内有且只有一个代表。首先其中至少有一个代表这是显然的,最小的那个一定是个代表。接下来考虑唯一性:采取反证法,如果存在两个代表,那么他们必定不在同一行或者同一列,我们假设是下面这种情况:

B.D
.#.
A.C

其中 \(A,D\) 是两个代表,我们假设 \(A<D\)\(B,C\) 是两个可能存在的交叉点,因为两者连通,所以 \(B,C\) 至少有一个存在,不妨设 \(B\) 存在。因为 \(A<B\) ,所以 \(A\) 行肯定要比 \(B\) 行要小,所以 \(B,D\) 之间存在的路径 \(A,C\) 之间也一定存在,同样的道理,\(C,D\) 之间的路径也一定存在。所以 \(C\) 一定存在,那么 \(A\) 所在列一定比 \(C\) 所在列要小,那么 \(B\) 就应该比 \(D\) 小,这与 \(D\) 是代表相悖,所以只存在一个代表。接下来数代表就好了!我们考虑每一个 \(a_i\) 能和多少 \(b\) 形成代表,我们只需要所有比 \(a_i\) 小的 \(a_j\)\(a_i\) 之间存在一个 \(a_k+b_j>x\) 即可,显然选择最大的。我们设 \(L_i\)\(i\) 左边最靠近 \(i\) 的满足 \(a_j<a_i\)\(j\) ,类似的定义 \(R_i\) ,那么定义 \(na_i=\min(\max_{j=L_i}^ia_j,\max_{j=i}^{r_i}a_j)\) (意思就是那个最大的可能阻断的 \(a_k\) ),类似定义 \(nb_i\) ,接下来只需要满足三个条件这两个就可以配对:

\[\begin{cases}a_i+b_j\le x\\na_i+b_i>x\\a_i+nb_i>x\end{cases} \]

稍微移项一下就得到了对于每个 \(a_i\)\(b\) 的取值范围以及 \(b_i\)\(a\) 的取值范围,这样直接把 \(b\) 左端点右端点以及 \(a\) 的值做扫描线+树状数组就好了。

代码中的一些 \(INF\) 是用来处理相等时的边界问题的。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int lg[MAXN];
struct st_list{
	int mx[MAXN][20];
	void init(int *a,int n)
	{
		FUP(i,1,n) mx[i][0]=a[i];
		FUP(j,1,19) FUP(i,1,n+1-(1<<j)) mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
	}
	int query(int ql,int qr)
	{
		int base=lg[qr-ql+1];
		return max(mx[ql][base],mx[qr-(1<<base)+1][base]);
	}
}Ta,Tb;
int sta[MAXN],tpf;
void stawork(int *a,int *l,int *r,int n)
{
	tpf=0;
	FUP(i,1,n)
	{
		while(tpf&&a[sta[tpf]]>a[i]) r[sta[tpf]]=i-1,tpf--;
		l[i]=sta[tpf]+1,sta[++tpf]=i;
	}
	while(tpf) r[sta[tpf]]=n,tpf--;
}
int tot;
struct Query{
	int opt,pos,x,y;
}que[MAXN*8];
bool cmp(Query x,Query y)
{
	if(x.pos<y.pos) return true;
	if(x.pos>y.pos) return false;
	if(x.opt<y.opt) return true;
	return false;
}
int n,m,X,a[MAXN],b[MAXN],La[MAXN],Lb[MAXN],Ra[MAXN],Rb[MAXN];
ll ans;
int fw[MAXN];
int lbt(int x){return x&(-x);}
void update(int x,int d){for(;x<=X;x+=lbt(x))fw[x]+=d;}
int query(int x)
{
	int ret=0;
	for(;x;x-=lbt(x)) ret+=fw[x];
	return ret;
}
int main(){
	n=read()+2,m=read()+2,X=read(),lg[0]=-1,a[1]=a[n]=b[1]=b[m]=INF;
	FUP(i,1,max(n,m)) lg[i]=lg[i>>1]+1;
	FUP(i,2,n-1) a[i]=read();
	FUP(i,2,m-1) b[i]=read();
	Ta.init(a,n),Tb.init(b,m);
	stawork(a,La,Ra,n),stawork(b,Lb,Rb,m);
	FUP(i,2,n-1)
	{
		La[i]=max(0,X-min(Ta.query(La[i],i),Ta.query(i,Ra[i])));
		Ra[i]=max(0,X-a[i]);
		que[++tot].opt=1,que[tot].pos=a[i],que[tot].x=La[i],que[tot].y=Ra[i];
	}
	FUP(i,2,m-1)
	{
		Lb[i]=max(0,X-min(Tb.query(Lb[i],i),Tb.query(i,Rb[i])));
		Rb[i]=max(0,X-b[i]);
		que[++tot].opt=2,que[tot].pos=Lb[i],que[tot].x=b[i],que[tot].y=1;
		que[++tot].opt=2,que[tot].pos=Rb[i],que[tot].x=b[i],que[tot].y=-1;
	}
	sort(que+1,que+tot+1,cmp);
	FUP(i,1,tot)
	{
		Query p=que[i];
		if(p.opt==1) ans+=query(p.y)-query(p.x);
		else update(p.x,p.y);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-08-21 16:27  Pbri  阅读(89)  评论(0编辑  收藏  举报