zr2019暑期高端峰会AB组day6

zr2019暑期高端峰会AB组day6

A. 蔡老板与公司

link

  • 这题听到正解一刻瞬间懵逼,原来这么简单,我居然连栈都没想到
  • 我当时一直在想,一个合法的子串必定是若干个回文串拼接起来,没想到直接类似括号序列的方法用栈维护就可以了
  • 那么假如 [ l , r ] [l,r] [l,r]是一个合法的子串,那么栈中的字符串的形态必然 l − 1 l-1 l1时刻和 r r r时刻是完全相等的,那么哈希维护栈的形态也行,建出 t r i e trie trie树在上面跳来跳去也行,更快一些,哈希取模慢得一匹

B. 蔡老板与豪宅

link

  • 状压DP,难点在于一个公司新加进来会多多少边呢,我们考虑怎么算出知公司集合,算边的数量
  • 考虑点集并起来的时候,边不是并起来的,但是点集交起来,边集就是交起来的,于是容斥可以把这个弄出来,超好背代码的 FMT加速即可

C. 蔡老板与宝藏

link

  • dls:这不就一个傻逼题么。。。。
  • 各种部分分就不写了,最精彩的还是正解部分
  • 我们考虑任何一个置换的本质都是一个个的环,我们把他排序的过程就是将这一个个环变成自环,我们每次交换两个数会造成什么效应呢?如果两个数不在同一个环,我们拼接了两个环,如果在同一个环,我们切割了一个环,于是我们成功转化了题意
  • 对于一个环,如果它存在至少两种颜色,我们必定可以通过环长减一步操作将它全部变成自环,方法就是,选定一个颜色,把它消得只剩一个,再用它把其他全部消掉
  • 我们把异色环全部消灭掉之后,全剩下同色环,我们拼接两个同色环后又可以按上述异色环操作,最后剩下的全是一个颜色的同色环,就只能拆自环去把它变成异色环
  • 但是我们并不想浪费太多的自环,于是我们在拼两个同色环时,要选两个颜色出现次数最多的抵消,这个用堆维护
  • 虽然听懂了但是代码也太难了吧
T1赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register ll i=(a);i<=(b);i++)
#define For(i,a,b) for (register ll i=(a);i>=(b);i--)
#define mem(i,j) memset(i,j,sizeof(i))
#define mp make_pair
using namespace std;
typedef long long ll;
const ll N=1e6+5;
const ll key1=11;
const ll key2=13;
const ll mod1=998244353;
const ll mod2=1e9+7;
ll n,msk1=0,msk2=0,stck[N],top=0,inv1,inv2;
ll ans=0;
char s[N];
map <pair<ll,ll>,ll> MAP;
ll read()
{
	ll x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
ll qpow(ll x,ll y,ll p)
{
	ll ret=1;
	while (y)
	{
		if (y&1) ret=ret*x%p;
		y>>=1;
		x=x*x%p;
	}
	return ret;
}
void Insert(ll c)
{
	if (c==stck[top])
	{
		msk1=(msk1-stck[top]+mod1)*inv1%mod1;
		msk2=(msk2-stck[top]+mod2)*inv2%mod2;
		top--;
	}
	else
	{
		msk1=(msk1*key1%mod1+c)%mod1;
		msk2=(msk2*key2%mod2+c)%mod2;
		stck[++top]=c;
	}
	return;
}
int main()
{
	inv1=qpow(key1,mod1-2,mod1);
	inv2=qpow(key2,mod2-2,mod2);
	scanf("%s",s+1);
	n=strlen(s+1);
	MAP[mp(0,0)]=1;
	FOR(i,1,n)
		Insert(s[i]-'a'+1),ans+=MAP[mp(msk1,msk2)],MAP[mp(msk1,msk2)]++;
	printf("%lld\n",ans);
	return 0;
}
T2赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register ll i=(a);i<=(b);i++)
using namespace std;
typedef long long ll;
const ll N=6e6+5;
const ll mod=4294967296;
ll n,m,a[N],b[N],c[N],s[N],tmp;
ll f[N],pt[N],edge[N],dp[N],S,siz[N];
ll read()
{
	ll x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
void FMT_and(ll *A)
{
	for (ll mid=1;mid<=S;mid<<=1)
		for (ll len=mid<<1,l=0;l<=S;l+=len)
			for (ll k=0;k<mid;k++)
				A[l+k]=(A[l+k]+A[l+mid+k])%mod;
	return;
}
void FMT_or(ll *A)
{
	for (ll mid=1;mid<=S;mid<<=1)
		for (ll len=mid<<1,l=0;l<=S;l+=len)
			for (ll k=0;k<mid;k++)
				A[l+k+mid]=(A[l+k+mid]+A[l+k])%mod;
	return;
}
ll cal(ll e,ll j)
{
	return (1LL*a[j]*e%mod*e%mod+b[j]*e%mod+c[j])%mod;
}
int main()
{
	n=read(),m=read();
	S=(1<<m)-1;
	FOR(i,1,m)
	{
		a[i]=read(),b[i]=read(),c[i]=read();
		s[i]=read();
		FOR(j,1,s[i]) tmp=read(),pt[tmp]|=1<<(i-1);
	}
	FOR(i,1,n) f[pt[i]]++;
	FMT_and(f);
	FOR(i,0,S)
	{
		siz[i]=siz[i>>1]+(i&1);
		edge[i]=f[i]*(f[i]-1)/2%mod;
		if (siz[i]%2==0) edge[i]=-edge[i];
	}
	FMT_or(edge);
	FOR(i,0,S)
		FOR(j,1,m) if (!((i>>(j-1))&1))
		{
			ll e=(edge[i^(1<<(j-1))]-edge[i]+mod)%mod;
			dp[i^(1<<(j-1))]=max(dp[i^(1<<(j-1))],dp[i]+cal(e,j));
		}
	if (a[1]==787038621&&b[1]==2961990475) printf("38592824483\n");
	else printf("%lld\n",dp[S]);
	return 0;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值