1400F
暴搜一下如果一个串的和为x且其中不存在一个子串
#include<bits/stdc++.h>
using namespace std;
int cnt;
int a[10010];
void dfs(int x,int now,int y)
{
if(now==0)
{
a[++cnt]=y;
return ;
}
for(int i=1;i<=9;i++)
{
int tmp=y;
int flag=1;
int sum=i;
if(x%sum==0) continue;
while(tmp)
{
sum+=tmp%10;
tmp/=10;
if(x%sum==0&&x!=sum)
{
flag=0;
break;
}
}
if(flag&&now-i>=0) dfs(x,now-i,y*10+i);
}
}
int main()
{
for(int i=1;i<=20;i++)
{
cnt=0;
dfs(i,i,0);
printf("%d\n",cnt);
}
}
不要担心他的复杂度!20个x-prime总共用了9896次dfs
0
0
0
0
2
0
7
2
7
7
54
2
139
23
40
15
928
6
2399
29
可以发现最多也只是x=19时,有2399个x-prime串,现在我们需要删除查询串,使他不包括这2399个串,那先对这些短查询串建一个ac自动机,状态数最多是4852
考虑dp
dp[i][j]表示当到了查询串s[i]位置在状态j时要的最少修改次数
显然存在删与不删两种情况,其中非x-prime串可以不删
转移方程于是可以得出
dp[i][j]=min(dp[i-1][j]+1,dp[i-1][fa[j]])
#include<bits/stdc++.h>
using namespace std;
int cnt;
int ttt,cnt1,x,len;
int a[2510];
int tr[5010][10];
int ix[5010];
int dp[2010][5000];
int fail[5010];
char c[3010];
void dfs(int x,int now,int y)
{
ttt++;
if(now==0)
{
a[++cnt]=y;
return ;
}
for(int i=1;i<=9;i++)
{
int tmp=y;
int flag=1;
int sum=i;
if(x%sum==0&&x!=sum) continue;
while(tmp)
{
sum+=tmp%10;
tmp/=10;
if(x%sum==0&&x!=sum)
{
flag=0;
break;
}
}
if(flag&&now-i>=0) dfs(x,now-i,y*10+i);
}
}
void insert(int x)
{
int rt=0;
while(x)
{
int now=x%10;
x/=10;
if(!tr[rt][now]) tr[rt][now]=++cnt1;
rt=tr[rt][now];
}
ix[rt]=1;
}
void build()
{
queue<int> q;
for(int i=0;i<10;i++)
{
if(tr[0][i])
{
fail[tr[0][i]]=0;
q.push(tr[0][i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
int ff=fail[now];
ix[now]|=ix[ff];
for(int i=0;i<10;i++)
{
if(!tr[now][i])
{
tr[now][i]=tr[ff][i];
}
else
{
fail[tr[now][i]]=tr[ff][i];
q.push(tr[now][i]);
}
}
}
}
int main()
{
scanf("%s%d",c+1,&x);
len=strlen(c+1);
dfs(x,x,0);
for(int i=1;i<=cnt;i++)
{
insert(a[i]);
}
build();
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=len;i++)
{
for(int j=0;j<=cnt1;j++)
{
if(dp[i-1][j]>len+1) continue;
dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
int son=tr[j][c[i]-'0'];
if(!ix[son])
{
dp[i][son]=min(dp[i][son],dp[i-1][j]);
}
}
}
int ans=len+1;
for(int i=0;i<=cnt1;i++)
{
ans=min(ans,dp[len][i]);
}
printf("%d\n",ans);
}
1400G
考虑容斥,满足的方案是限制中只选一个或者都不选,相反条件是都选,用总贡献减去都选的贡献即可。
都选就转化为了状压枚举容斥限制,这个复杂度为
2
20
2^{20}
220,合法。
然后如果要每个限制都满足,选的人数必然要在所有限制包含的人的要求人数的交集内。
假设交集是
[
l
,
r
]
[l,r]
[l,r],限制选的总人数是x则这一块的贡献是
∑
i
=
l
r
(
i
−
x
s
u
m
i
−
x
)
\sum_{i=l}^{r}(_{i-x}^{sumi-x})
∑i=lr(i−xsumi−x)发现x很小,可以先预处理一个前缀和
于是本题结束
#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
int n;
int m;
int xx[22],yy[22];
int vis[300010];
int p[300010];
int ll[300010],rr[300010];
long long fac[300010],inv[300010];
long long sum[300010][41];
long long kasumi(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
long long c(int x,int y)
{
if(x<0||y<0) return 0;
if(x<y) return 0;
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
scanf("%d%d",&n,&m);
fac[0]=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&ll[i],&rr[i]);
p[ll[i]]++,p[rr[i]+1]--;
fac[i]=fac[i-1]*i%mod;
}
inv[n]=kasumi(fac[n],mod-2);
for(int i=n-1;i>=0;i--)
{
inv[i]=inv[i+1]*(i+1)%mod;
}
for(int i=0;i<m;i++)
{
scanf("%d%d",&xx[i],&yy[i]);
}
for(int i=1;i<=n;i++)
{
p[i]=p[i]+p[i-1];
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m*2;j++)
{
sum[i][j]=sum[i-1][j]+c(p[i]-j,i-j);
sum[i][j]%=mod;
}
}
long long ans=0;
for(int i=1;i<(1<<m);i++)
{
int cnt=0;
int tmpp=0;
int l=0,r=n;
for(int j=0;j<m;j++)
{
if(i&(1<<j))
{
tmpp++;
if(!vis[xx[j]]) cnt++,vis[xx[j]]=1,l=max(l,ll[xx[j]]),r=min(r,rr[xx[j]]);
if(!vis[yy[j]]) cnt++,vis[yy[j]]=1,l=max(l,ll[yy[j]]),r=min(r,rr[yy[j]]);
}
}
if(tmpp&1)
{
if(r>=l) ans=(ans+sum[r][cnt]-sum[l-1][cnt]+mod)%mod;
}
else
{
if(r>=l) ans=(ans-sum[r][cnt]+sum[l-1][cnt]+mod)%mod;
}
for(int j=0;j<m;j++)
{
if(i&(1<<j))
{
vis[xx[j]]=0;
vis[yy[j]]=0;
}
}
}
ans=(mod-ans)%mod;
for(int i=1;i<=n;i++)
{
ans=(ans+c(p[i],i))%mod;
}
printf("%lld\n",ans);
}