8.20NOIP Day6模拟赛
T1
容易观察到,只有出现?io?时可能使用交换操作,其他情况可以被另外三个所取代,所以特判一下即可
然后还需要更新当前原字母的贡献,同时对其他三个操作各跑一次转移,力大砖飞即可
想打表也没人拦着你
#include<bits/stdc++.h>
#define N 100005
using namespace std;
char s[N];
int x[N],dp[N][10];
signed main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s",s+1);
s[0]=' ';
int n=strlen(s),ans=4;
n--;
for(int i=1;i<=n;i++){
if(s[i]=='n')x[i]=1;
else if(s[i]=='o')x[i]=2;
else if(s[i]=='i')x[i]=3;
else if(s[i]=='p')x[i]=4;
else x[i]=0;
}
for(int i=0;i<=n;i++)for(int j=0;j<=4;j++)dp[i][j]=j;
for(int i=1;i<=n;i++){
if(x[i])dp[i][x[i]]=min(dp[i][x[i]],dp[i-1][x[i]-1]);
if(i>=2&&x[i]==2&&x[i-1]==3)dp[i][3]=min(dp[i][3],dp[i-2][1]+1);
for(int j=4;j;j--)dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1);
for(int j=1;j<=4;j++)dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
for(int j=1;j<=4;j++)dp[i][j]=min(dp[i][j],dp[i][j-1]+1);
ans=min(ans,dp[i][4]);
}
printf("%d\n",ans);
}
return 0;
}
T2
考虑正难则反
显然的,正常情况下一个点无法被到达当且仅当它的上方和左方都有障碍
那么我们按照行数为第一关键字排序,列数为第二关键字排序,计算每一个有障碍的行有多少格子无法被抵达,需要线段树维护后缀有障碍的列数
同时需要对第一行或第一列有障碍的情况进行特判
#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
struct Ty{
int x,y;
bool operator <(const Ty &a)const{
if(x!=a.x)return x<a.x;
else return y>a.y;
}
}x[N];
vector<int>w[N];
int y[530000];
void update(int u,int l,int r,int id){
if(l==r){
y[u]=1;
return;
}
int mid=(l+r)/2;
if(id<=mid)update(u*2,l,mid,id);
else update(u*2+1,mid+1,r,id);
y[u]=y[u*2]+y[u*2+1];
return;
}
int query(int u,int l,int r,int fl,int fr){
if(l==fl&&r==fr)return y[u];
int mid=(l+r)/2;
if(fr<=mid)return query(u*2,l,mid,fl,fr);
else if(fl>mid)return query(u*2+1,mid+1,r,fl,fr);
else return query(u*2,l,mid,fl,mid)+query(u*2+1,mid+1,r,mid+1,fr);
}
signed main(){
int n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=k;i++)scanf("%lld%lld",&x[i].x,&x[i].y);
sort(x+1,x+k+1);
for(int i=1;i<=k;i++)w[x[i].x].push_back(x[i].y);
int ans=0,flag=0;
if(w[1].size())for(int i=w[1][w[1].size()-1];i<=m;i++)update(1,1,m,i);
for(int i=1;i<=n;i++){
for(int j=0;j<w[i].size();j++)update(1,1,m,w[i][j]);
if(flag)ans+=query(1,1,m,1,m);
else if(w[i].size()){
ans+=query(1,1,m,w[i][w[i].size()-1],m);
if(w[i][w[i].size()-1]==1)flag=1;
}
}
printf("%lld\n",n*m-ans);
return 0;
}
T3
题目可以等效为不超过 \(n\) 位的十进制中有多少个 \(p\) 的倍数满足所有位之和不超过 \(m\)
朴素的DP就是从小往大枚举每一位、这一位的值,前一位的值和对 \(p\) 取模的结果进行转移
对于所有 \({10}^i \mod p={10}^j \mod p\) 我们可以将两者合并,他们在 \(\mod p\) 意义下贡献相同,这样的话最多剩下 \(p\) 组
对每一组按原方法进行 DP ,此时我们需要求该组各位总和为 \(i\) 有多少种情况,需要容斥
容斥式子是
\[\sum_{i=1}^{\min(m,\lfloor n/10\rfloor)}(-1)^i\begin{pmatrix}m\\i\end{pmatrix}\begin{pmatrix}m+n-10i-1\\n-10i\end{pmatrix}
\]
#include<bits/stdc++.h>
#define mod 998244353
#define N 105
#define int long long
using namespace std;
int x[N],dp[N][N][N*10],cnt[N],y[N],C[N][N*10],f[N][N*10];
int mpow(int a,int p){
int ans=1;
while(p){
if(p&1)ans=ans*a%mod;
a=a*a%mod;
p=p/2;
}
return ans;
}
signed main(){
int n,p,m,flag=1,check=1;
scanf("%lld%lld%lld",&n,&p,&m);
x[1]=1;
cnt[1]=1;
for(int i=2;i<=n;i++){
x[i]=x[i-1]*10%p;
if(cnt[x[i]]){
check=cnt[x[i]];
break;
}
cnt[x[i]]=i;
flag=i;
}
for(int i=check;i<=flag;i++)y[i]=(n-i)/(flag-check+1)+1;
for(int i=1;i<check;i++)y[i]=1;
dp[0][0][0]=1;
for(int i=1;i<=flag;i++){
for(int j=0;j<=m;j++){
int now=1;
for(int h=y[i];h<=y[i]+j-1;h++)now=now*h%mod;
for(int h=1;h<=j;h++)now=now*mpow(h,mod-2)%mod;
C[i][j]=now;
}
for(int j=0;j<=m;j++){
int now=1;
for(int h=y[i]-j+1;h<=y[i];h++)now=now*h%mod;
for(int h=1;h<=j;h++)now=now*mpow(h,mod-2)%mod;
f[i][j]=now;
}
for(int j=0;j<=m;j++){
int now=C[i][j];
for(int k=1;k*10<=j&&k<=y[i];k++){
now=(now+mod+C[i][j-k*10]*f[i][k]%mod*(k%2?-1:1))%mod;
}
for(int k=0;k<=m-j;k++){
for(int l=0;l<p;l++){
dp[i][(l+j*x[i])%p][k+j]=(dp[i][(l+j*x[i])%p][k+j]+dp[i-1][l][k]*now%mod)%mod;
}
}
}
}
int ans=0;
for(int i=0;i<=m;i++){
ans=(ans+dp[flag][0][i])%mod;
printf("%lld ",ans);
}
return 0;
}

浙公网安备 33010602011771号