C - Stones
枚举分界点爆算即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int N=200005;
int n;
char s[N];
int sum[N][2];
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
sum[0][0]=sum[0][1]=0;
for(int i=1;i<=n;i++)
{
sum[i][0]=sum[i-1][0],sum[i][1]=sum[i-1][1];
if(s[i]=='.') sum[i][0]++;
else if(s[i]=='#') sum[i][1]++;
}
int ans=n;
for(int i=0;i<=n;i++)
ans=min(ans,(i-sum[i][0])+(n-i-(sum[n][1]-sum[i][1])));
printf("%d",ans);
return 0;
}
D - Three Colors
考虑计算不合法的方案。
令 \(f_{i,j}\) 表示考虑前 \(i\) 个数,\(c\) 的集合中的数的和为 \(j\) 的方案数。转移分成分到 \(a,b\) 或者分到 \(c\) 讨论。
如果直接计算会发现 \(m\) 为偶数时 \(c\) 的大小为 \(\frac{m}{2}\) 会被多算,再 DP 一遍减去就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=305;
const int MOD=998244353;
int n,m;
int a[N];
long long f[N][N*N],g[N][N*N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),m+=a[i];
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j]*2%MOD;
if(j>=a[i]) f[i][j]=(f[i][j]+f[i-1][j-a[i]])%MOD;
}
long long ans=1;
for(int i=1;i<=n;i++)
ans=ans*3%MOD;
for(int j=(m+1)/2;j<=m;j++)
ans=(ans-f[n][j]*3%MOD+MOD)%MOD;
if(m%2==0)
{
g[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
g[i][j]=g[i-1][j];
if(j>=a[i]) g[i][j]=(g[i][j]+g[i-1][j-a[i]])%MOD;
}
ans=(ans+g[n][m/2]*3%MOD)%MOD;
}
printf("%lld",ans);
return 0;
}
E - Polynomial Divisors
\(f(i)\equiv 0 \pmod p\) 相当于 \(f(i)\) 在模 \(p\) 意义下存在因式 \((x -i)\)。
则 \(\forall i\in N,f(i)\equiv 0\pmod p\) 等价于 \(f ( i )\) 在模 $p $ 意义下存在因式 \(\prod\limits_{i=0}^{p-1}(x-i)\equiv x^p+\cdots \ +\prod\limits_{i=0}^{p-1}(-i) \pmod p\),因为 \(x\) 的指数需要小于等于 \(n\),故 \(p\) 为 \(a_i\) 的公因数或者 \(p\leq n\)。
当 \(x\neq 0\) 时,有 \(x^{p-1}\equiv 1 \pmod p\),则:
\[\begin{aligned}f(x)&= a_nx^n+a_{n-1}x^{n-1}+\cdots +a_0 \\ &= x^0(a_0+a_{p-1}x^{p-1}+a_{2(p-1)}x^{2(p-1)}+\cdots )+x^1(a_1+a_px^{p-1}+a_{2(p-1)+1}x^{2(p-1)}+\cdots )+\cdots \\ &=x^0(a_0+a_{p-1}+a_{2(p-1)}+\cdots ) + x^1(a_1+a_p+a_{2(p-1)+1}+\cdots ) \cdots \end{aligned} \]只有当 $a_0+a_{p-1}+a_{2(p-1)}+\cdots ,a_1+a_p+a_{2(p-1)+1}+\cdots ,\ldots $ 均为 \(0\) 时才能满足 \(\forall x,f(x)\equiv 0\pmod p\)。
对于 \(x=0\) 的情况,\(f(x)=a_0\),需要满足 \(p|a_0\)。
暴力枚举 \(p\),\(O(n)\) 判断是否合法即可。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=10005;
int n;
int a[N];
bool isprime[N];
int prime[N],tot;
void init(int n=10000)
{
memset(isprime,true,sizeof(isprime));
isprime[1]=false;
for(int i=2;i<=n;i++)
{
if(isprime[i]) prime[++tot]=i;
for(int j=1;j<=tot&&i*prime[j]<=n;j++)
{
isprime[i*prime[j]]=false;
if(i%prime[j]==0) break;
}
}
return;
}
bool check(int p)
{
if(a[0]%p!=0) return false;
static int sum[N];
for(int i=0;i<=p-2;i++)
sum[i]=0;
for(int i=0;i<=n;i++)
sum[i%(p-1)]=(sum[i%(p-1)]+a[i])%p;
for(int i=0;i<=p-2;i++)
if(sum[i]) return false;
return true;
}
int main()
{
init();
scanf("%d",&n);
for(int i=n;i>=0;i--)
scanf("%d",&a[i]);
int k=a[0];
for(int i=1;i<=n;i++)
k=__gcd(k,a[i]);
k=abs(k);
vector<int>res;
for(int i=1;i<=tot&&prime[i]<=sqrt(k);i++)
if(k%prime[i]==0)
{
res.push_back(prime[i]);
while(k%prime[i]==0) k/=prime[i];
}
if(k>1) res.push_back(k);
for(int i=1;i<=tot&&prime[i]<=n;i++)
{
int p=prime[i];
if(check(p)) res.push_back(p);
}
sort(res.begin(),res.end());
res.erase(unique(res.begin(),res.end()),res.end());
for(int p:res)
printf("%d\n",p);
return 0;
}
Banned X
因为 \(0\) 不会对总和产生影响,考虑枚举 \(0\) 的个数 \(i\) 计算出方案 \(f(n-i)\) 再将 \(0\) 插入。
考虑合法的 \(1,2\) 的串需要满足其中一种情况:
- 序列的和不超过 \(X\);
- 序列中只有 \(2\);
- 序列的和为奇数,序列的中的任意一个 \(1\),它和左边的数的和 \(< X\),它和右边的数的和 \(< X\)。
可以发现,序列中一定会有 \(x-1\) 的一段,则剩下的数一定都为 \(2\)。
具体证明的话可以发现这段的右边第一个肯定为 \(2\),如果这段的左端点为 \(1\) 的话就不满足条件。故这段的左端点为 \(2\),那么这个区间就可以往右移直到 \(1\) 位置就不合法了。第三种情况的证明同理。
对于第 1,2 种情况,可以用组合数算出。
对于第 3 种情况,序列右侧和左侧都应有至少 \(\frac{S-X+1}{2}\) 个 \(2\),中间可任意摆放。
证明的话可以考虑前缀的 \(2\) 小于后缀的 \(2\) 的情况,我们可以一直向右移动到左边第一个 \(1\),这就不合法了。前缀的 \(2\) 大于后缀的 \(2\) 的情况同理。
#include<iostream>
#include<cstdio>
using namespace std;
const int N=3005;
const int MOD=998244353;
int n,X;
long long ksm(long long a,long long b)
{
long long res=1;
while(b)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD,b>>=1;
}
return res;
}
long long fac[N],inv[N];
void init(int n=3000)
{
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%MOD;
inv[n]=ksm(fac[n],MOD-2);
for(int i=n;i>=1;i--)
inv[i-1]=inv[i]*i%MOD;
return;
}
long long C(int n,int m)
{
if(m>n) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
long long f(int n)
{
long long res=0;
if(2*n<X||(2*n-X)%2==1) res++;
for(int S=0;S<2*n;S++)
{
if(S<X)
{
if(S>=n) res=(res+C(n,S-n))%MOD;
}
else
{
if((S-X)%2==0) continue;
int num=(S-X+1)/2;
int reb=n-2*num,ret=S-2*num*2;
if(reb<=0||ret<=0) continue;
if(ret>=reb&&ret<=2*reb) res=(res+C(reb,ret-reb))%MOD;
}
}
return res;
}
int main()
{
init();
scanf("%d%d",&n,&X);
long long ans=0;
for(int i=0;i<=n;i++)
ans=(ans+C(n,i)*f(n-i)%MOD)%MOD;
printf("%lld",ans);
return 0;
}
标签:int,res,long,Tenka1,cdots,2019,Programmer,include,MOD
From: https://www.cnblogs.com/zhou-jk/p/17734111.html