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
不会,咕了。