codeforces 1009G Allowed Letters

可耻地看了题解。。。本来想直接网络流的,然而不知道怎么得到最小的解,看网上有退流的做法可以求字典序最小的最小割集,不过那个退流要每次删掉边在跑增广路,至少是n^2的复杂度,这里1e5的点肯定是不行的。

于是想起cf可以看别人代码,于是肮脏地抄起了代码。发现了一种十分神奇的写法,a-f记为0-5,num[c]为字母c剩余的个数,st[i]为第i个位置可以放哪些字母的状态压缩,sum[i][s]记录从i到n有多少st[i]是s的子集。于是对于每个位置,枚举这个位置填的字母为c=0-5,num[c]--,如果对于所有状态s,其中所有字母的剩余个数总共为ssum,ssum全都>=sum[i+1][s],这就说明,当前位置放置c这个字母是合理的,因为如果ssum<sum[i+1][s]说明这个状态s的字母的总数如果小于st[i]是s的子集的i的数量,就因为st[i]是s的子集,就说明i这个位置必须填状态s的某一个字母,而总数又不够,说明是不行的。

复杂度为n*6*(1<<6)*6大约为2*1e8,而且由于st[i]的限制,总量会比这个更少,而且cf的评测姬很好,2s的时限是完全够的。结果显示我的时间是155ms。

#include<cstdio>
#include<cstring>
#define maxl 100010

const int cnt=(1<<6)-1;

int n,m,ans;
int st[maxl],son[cnt+10],num[6];
int sum[maxl][cnt+10];
char s[maxl],ch[7],anss[maxl];

inline void prework()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++)
		num[s[i]-'a']++;
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int pos;
		scanf("%d",&pos);
		scanf("%s",ch+1);
		int l=strlen(ch+1);
		for(int j=1;j<=l;j++)
		{
			int c=ch[j]-'a';
			st[pos]|=(1<<c);
		}
	}
	for(int i=1;i<=n;i++)
	if(st[i]==0)
		st[i]=cnt;
	for(int i=n;i>=1;i--)
		for(int j=1;j<=cnt;j++)
		{
			if((st[i]&j)==st[i])
				son[j]++;
			sum[i][j]=son[j];
		}
}

inline void mainwork()
{
	int ssum=0;bool flag;
	for(int i=1;i<=n;i++)
	{
		ans=0;
		for(int c=0;c<6;c++)
		{
			if(num[c]==0 || (st[i]&(1<<c))==0)
				continue;
			num[c]--;
			flag=true;
			for(int s=1;s<=cnt && flag;s++)
			{
				ssum=0;
				for(int j=0;j<6;j++)
				if(s&(1<<j))
					ssum+=num[j];
				if(ssum<sum[i+1][s])
					flag=false;
			}
			if(flag)
			{
				ans=1;anss[i]=c+'a';
				break;
			}
			num[c]++;
		}
		if(!ans)
			return;
	}
}

inline void print()
{
	if(ans)
		for(int i=1;i<=n;i++)
			printf("%c",anss[i]);
	else
		printf("Impossible");
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

dinic版占坑。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值