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,0)+f(x,1)+f(x,2)\) 本质上是对第 \(x\) 列的前 \(3n+2\) 项求和,所以可以得到
然后你解一下方程就可以知道以下递推式( \(f(x,2)\) 没有用了,只是用来推导的)
\(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\) 是面积,\(b\) 是边界上的点,\(a\) 是内部的点。
以及面积公式:
其中三个点的坐标分别是 \((a,b),(c,d),(e,f)\) 。
以及求边上点的个数公式:
以及关于 \(\gcd\,mod4\) 的公式:
( \(\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\) ,接下来只需要满足三个条件这两个就可以配对:
稍微移项一下就得到了对于每个 \(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;
}