ARC 115 题解

A

显然如果 \(x\)\(y\) 有偶数个位置不同,那么一定是可以有相同数量的。

那么只要考虑有奇数个位置不同的情况,那么方案数就是通过有奇数个 \(1\) 的和有偶数个 \(1\) 的配对来贡献的。

于是扫一遍统计一下就好了。

#include <bits/stdc++.h>
typedef long long ll;
const int N=1e5+10;
int n,m,c[2];
char s[100];
int main(){
	scanf("%d%d",&n,&m);
	int cnt=0;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1),cnt=0;
		for(int j=1;j<=m;++j)if(s[j]=='1')++cnt;
		++c[cnt%2];
	}
	// printf("%d %d\n",c[0],c[1]);
	printf("%lld\n",1ll*c[0]*c[1]);
	return 0;
}

B

观察发现若相邻两行或者两列的差值不同,那么是不存在符合的序列。

我们求出第一列的最小值,然后让 \(B\) 是第一列中的数字减去最小值,然后 \(A\) 通过左上角就可以计算出来。

#include <bits/stdc++.h>
typedef long long ll;
const int N=510;
int n,c[N][N],a[N],b[N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%d",&c[i][j]);
    for(int i=2;i<=n;++i){
        int t=c[i][1]-c[i-1][1];
        for(int j=2;j<=n;++j)if(c[i][j]-c[i-1][j]!=t){
            puts("No");
            return 0;
        }
    }
    for(int j=2;j<=n;++j){
        int t=c[1][j]-c[1][j-1];
        for(int i=2;i<=n;++i)if(c[i][j]-c[i][j-1]!=t){
            puts("No");
            return 0;
        }
    }
    int mn=(1<<30);
    for(int i=1;i<=n;++i)mn=std::min(mn,c[1][i]);
    for(int i=1;i<=n;++i){
        b[i]=c[1][i]-mn;
        a[i]=c[i][1]-c[1][1]+mn;
    }
    for(int i=1;i<=n;++i)if(a[i]<0||b[i]<0){
        puts("No");
        return 0;
    }
    puts("Yes");
    for(int i=1;i<=n;++i)printf("%d ",a[i]);
    puts("");
    for(int i=1;i<=n;++i)printf("%d ",b[i]);
    puts("");
    return 0;
}

C

注意到我们可以通过质因数个数 \(+1\) 来构造答案,这样一定是最大值最小的。

为什么呢?我们设 \(2^k\)\(n\) 中最大的 \(2\) 的次幂,那么 \(a_1 , a_2 , \cdots , a_{2^k}\) 都要不一样,那么最大值至少要 \(k+1\),与我们所构造的相同。

#include <bits/stdc++.h>
typedef long long ll;
const int N=1e5+10;
int n,pr[N],tot,cnt[N];
bool vis[N];
void init(){
    cnt[1]=1;
    for(int i=2;i<=N-10;++i){
        if(!vis[i])pr[++tot]=i,cnt[i]=1;
        for(int j=1;j<=tot&&i*pr[j]<=N-10;++j){
            vis[i*pr[j]]=1,cnt[i*pr[j]]=cnt[i]+1;
            if(i%pr[j]==0)break;
        }
    }
}
int main(){
    init(),scanf("%d",&n);
    for(int i=1;i<=n;++i)(i==1)?printf("1 "):printf("%d ",cnt[i]+1);
    puts("");
    return 0;
}

D

注意到一个事实:一个图里奇度数的点的数量不会是偶数。

分三种情况考虑:

  • 两个非奇度数点连边,奇度数点加 \(2\),为偶数。
  • 两个奇度数点连边,奇度数点数减 \(2\),为偶数。
  • 一个奇度数点,一个非奇度数点连边,奇度数点数不变,为偶数。

所以只要考虑 \(k\) 为奇数的情况。

先考虑图为一棵树的情况,我们可以通过选出 \(k\) 个点,然后删掉这些点的叶子,删成奇度数,删法是唯一的,所以方案数是 \(\binom{\mid V \mid}{k}\)

当为连通图时,我们考虑一棵生成树,生成树的方案同上,剩下的 \(\mid E \mid - \mid V \mid + 1\) 条边每条都可以选或者不选,方案数为 \(2^{\mid E \mid - \mid V \mid + 1}\)

还要注意到一个细节,题目并没有保证给出的图是连通的,所以我们要对每个连通块分别计算。

\(f(i,j)\) 表示前 \(i\) 个连通块,最后一个选了 \(j\) 的点的方案数。

那么 \(f(i,j)=\displaystyle \sum_{k=0}^{\min(j,\mid V \mid)} f(i-1,j-k) \times 2^{\mid E \mid - \mid V \mid + 1} \times \binom{\mid V \mid}{k}\)

时间复杂度 \(\mathcal{O}(n^2)\),可以通过 NTT 做到 \(\mathcal{O}(n \log^2 n)\)

#include <bits/stdc++.h>
typedef long long ll;
const int N=5010;
const int mod=998244353;
int n,m,fa[N],cnte[N],sz[N];
ll f[N][N],fac[N],ifac[N];
void init(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(int i=2;i<=N-10;++i){
		fac[i]=1ll*i*fac[i-1]%mod;
		ifac[i]=1ll*(mod-mod/i)*ifac[mod%i]%mod;
	}
	for(int i=2;i<=N-10;++i)ifac[i]=ifac[i]*ifac[i-1]%mod;
}
ll binom(int n,int m){
	return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
ll fastpow(ll a,int b){
	ll ret=1;
	for(;b;b>>=1,a=a*a%mod)if(b&1)ret=ret*a%mod;
	return ret;
}
int main(){
	scanf("%d%d",&n,&m);
	init();
	for(int i=1;i<=n;++i)fa[i]=i,cnte[i]=0,sz[i]=1;
	for(int i=1;i<=m;++i){
		int u,v;scanf("%d%d",&u,&v);
		u=find(u),v=find(v);
		if(u==v)++cnte[u];
		else{
			fa[u]=v;
			sz[v]+=sz[u],cnte[v]+=cnte[u]+1;
		}
	}
	f[0][0]=1;
	int cnt=0;
	for(int i=1;i<=n;++i)if(fa[i]==i){
		++cnt;
		for(int j=0;j<=n;++j)for(int k=0;k<=std::min(j,sz[i]);k+=2)
			f[cnt][j]=(f[cnt][j]+f[cnt-1][j-k]*binom(sz[i],k)%mod*fastpow(2ll,cnte[i]-sz[i]+1)%mod)%mod;
			// printf("%d %d %d %d %lld\n",j,k,cnte[i],sz[i],binom(sz[i],k));
	}
	for(int i=0;i<=n;++i)printf("%lld\n",f[cnt][i]);
	return 0;
}

E

考虑容斥原理。

\(f(i)\) 表示做到 \(i\) 的答案,那么 \(f(i)=\displaystyle \sum_{j=1}^{i-1} f(j) \times \min_{k=j+1}^i a_k \times (-1)^{i-j-1}\)

那么我们通过单调栈求出 \(a_k\) 作为最小值的区间,然后维护一下 \(f\) 的和,区间乘法,单点加法,用线段树维护一下。

#include <bits/stdc++.h>
typedef long long ll;
#define gc() (_S==_T&&(_T=(_S=_B)+fread(_B,1,1<<20,stdin),_S==_T)?EOF:*_S++)
char _B[1<<20],*_S=_B,*_T=_B;
int rd(){
	int x=0,f=0;char ch=gc();
	while(ch<'0'||ch>'9')(ch=='-')&&(f=1),ch=gc();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=gc();
	return f?-x:x;
}
const int N=5e5+10;
const int mod=998244353;
int n,a[N],stk[N],cnt=0,tp=0;
ll f[N];
struct node{
	int l,r;
	ll tag,v,f;
	node *ls,*rs;
}tr[N<<2],*rt;
void pushup(node *o){
	o->v=(o->ls->v+o->rs->v)%mod;
	o->f=(o->ls->f+o->rs->f)%mod;
}
void pushdown(node *o){
	if(o->tag!=1){
		o->ls->f=o->ls->v*o->tag%mod;
		o->rs->f=o->rs->v*o->tag%mod;
		o->ls->tag=o->ls->tag*o->tag%mod;
		o->rs->tag=o->tag*o->rs->tag%mod;
		o->tag=1;
	}
}
void build(node *o,int l,int r){
	o->l=l,o->r=r,o->tag=1;
	if(l==r)return;
	int mid=(l+r)>>1;
	o->ls=&tr[++cnt],o->rs=&tr[++cnt];
	build(o->ls,l,mid),build(o->rs,mid+1,r);
}
void A(node *o,int p,ll v){
	if(o->l==o->r){
		o->v=v,o->f=v*o->tag%mod;
		return;
	}
	int mid=(o->l+o->r)>>1;
	pushdown(o);
	if(p<=mid)A(o->ls,p,v);
	else A(o->rs,p,v);
	pushup(o);
}
void M(node *o,int ql,int qr,ll v){
	if(ql<=o->l&&o->r<=qr){
		o->tag=v,o->f=o->v*v%mod;
		return;
	}
	int mid=(o->l+o->r)>>1;
	pushdown(o);
	if(ql<=mid)M(o->ls,ql,qr,v);
	if(qr>mid)M(o->rs,ql,qr,v);
	pushup(o);
}
int main(){
	n=rd(),rt=&tr[0],build(rt,1,n);
	for(int i=1;i<=n;++i)a[i]=rd();
	A(rt,1,mod-1),stk[++tp]=0;
	for(int i=1;i<=n;++i){
		while(tp&&a[stk[tp]]>=a[i])--tp;
		M(rt,stk[tp]+1,i,a[i]);
		stk[++tp]=i;
		f[i]=(i&1)?(mod-rt->f):rt->f;
		A(rt,i+1,(i&1)?f[i]:(mod-f[i]));
	}
	printf("%lld\n",f[n]);
	return 0;
}

F

不会,咕了。

posted @ 2022-05-20 22:19  Nylch  阅读(42)  评论(0编辑  收藏  举报