codeforces EDU94 订正

2 篇文章 0 订阅
1 篇文章 0 订阅

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(ixsumix)发现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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值