708 AIM Tech Round 3 (Div. 1)

A. Letters Cyclic Shift

白痴。注意全是a要把最后一个变成z

证明我写了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int _=1e2;
const int maxn=1e5+_;
 
char ss[maxn];
int main()
{
	scanf("%s",ss+1);int n=strlen(ss+1);
	int tp=1;
	while(ss[tp]=='a'&&tp<=n)tp++;
	for(int i=tp;i<=n;i++)
		if(ss[i]!='a')ss[i]--;
		else break;
	if(tp==n+1)ss[n]='z';
	for(int i=1;i<=n;i++)printf("%c",ss[i]);
	
	return 0;
}
B. Recover the String

题意:要求构造一个01序列,满足子序列(不是子串)00、01、10、11出现的次数满足给定数。序列长度不超过 1 e 6 1e6 1e6

从00和11的个数可以推出0和1的个数(注意特判个数=0的情况)

关键一步:10的个数+01的个数=0的个数*1的个数

也就是说能够构造出一种操作,令一个10变成01,可以说是数对的思想

开始先把0放到最前,1都放在最后,然后暴力调整即可

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=1e6+_;
 
int e[maxn];
bool solve(LL u,LL v,LL b,LL c)
{
	if(!u&&!v)return false;
	if(!u||!v)
	{
		if(!b&&!c)
		{
			int tt=0;
			if(!u)tt++,swap(u,v);
			for(int i=1;i<=u;i++)printf("%d",tt);
			return true;
		}
		else return false;
	}
	if(u*v!=b+c)return false;
	for(int i=1;i<=u;i++)e[i]=0;
	for(int i=u+1;i<=u+v;i++)e[i]=1;
	int gp=1,tp=u+1,p;
	while(c>=u)
	{
		swap(e[gp],e[tp]),gp++,tp++;
		if(tp==tp==u+v+2)return false;
		c-=u;
	}
	p=tp;tp++;
	while(c--)
	{
		if(p==gp)
		{
			p=tp,tp++;gp++;
			if(tp==u+v+2)return false;
		}
		swap(e[p],e[p-1]),p--;
	}
	for(int i=1;i<=u+v;i++)printf("%d",e[i]);
	return true;
}
 
LL C[maxn];int num[maxn];
void yu(){for(int i=1;i<maxn;i++)C[i]=(LL)i*(i-1)/2;}
int main()
{
	LL a,b,c,d;
	scanf("%lld%lld%lld%lld",&a,&b,&c,&d); yu();
	int u=lower_bound(C+1,C+maxn,a)-C;
	if(a!=0&&C[u]!=a){puts("Impossible");return 0;}
	for(u=(a==0)?0:u;;u++)
	{
		int v=lower_bound(C+1,C+maxn,d)-C;
		if(d!=0&&C[v]!=d){puts("Impossible");return 0;}
		for(v=(d==0)?0:v;;v++)
		{
			if(solve(u,v,b,c))return 0;
			if(v==1||d!=0)break;
		}
		if(u==1||a!=0)break;
	}
	puts("Impossible");
	
	return 0;
}
C. Centroids

题意:给一棵树,问树的每个点能不能通过仅一次删边加边变成质心,所谓质心即删掉该点和相邻的边剩下的每个联通块大小都小于等于n/2。 n ≤ 400000 n\leq400000 n400000

明显要换根了,对于当前点,假如不符合条件,那么一定是在那棵大于n/2的子树中拆掉一下接到当前点,每个子树维护一下当前子树中最大的不超过n/2的子树即可(代码中的mq)

对于父亲来的那棵子树,根到当前点链上的信息可以通过换根时处理,只要每次考虑去掉兄弟子树中的子树会不会更优即可

可以用dfs搞一个伪的bfs序,让兄弟的编号连续。把mq放到线段树,这样就可以用一个log搞定上面的问题。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int _=1e2;
const int maxn=4e5+_;
const int fbin=(1<<19)+_;
 
int pos[fbin],mq[maxn],tot[maxn];
namespace S
{
	#define lc (now<<1)
	#define rc (now<<1)|1
	#define mid (ql+qr)/2
	int tr[2*fbin];
	void bt(int now,int ql,int qr)
	{
		if(ql==qr){tr[now]=mq[pos[ql]];return ;}
		bt(lc,ql,mid),bt(rc,mid+1,qr);
		tr[now]=(tot[tr[lc]]>tot[tr[rc]])?tr[lc]:tr[rc];
	}
	int findmax(int now,int ql,int qr,int l,int r)
	{
		if(l>r)return 0;
		if(ql==l&&qr==r)return tr[now];
			 if(r<=mid)  return findmax(lc,ql,mid,l,r);
		else if(mid+1<=l)return findmax(rc,mid+1,qr,l,r);
		else 
		{
			int ll=findmax(lc,ql,mid,l,mid);
			int rr=findmax(rc,mid+1,qr,mid+1,r);
			return (tot[ll]>tot[rr])?ll:rr;
		}
	}
	#undef lc
	#undef rc
	#undef mid
}
 
int n,li;
struct node
{
	int x,y,next;
}a[2*maxn];int len,last[maxn];
void ins(int x,int y)
{
	len++;
	a[len].x=x;a[len].y=y;
	a[len].next=last[x];last[x]=len;
}
int z,id[maxn],L[maxn],R[maxn];
int mx[maxn],sx[maxn];
void dfs(int x,int fr)
{
	L[x]=z+1;
	for(int k=last[x];k;k=a[k].next)
		if(a[k].y!=fr)id[a[k].y]=++z,pos[z]=a[k].y;
	R[x]=z;
	
	tot[x]=1;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y!=fr)
		{
			dfs(y,x);
			tot[x]+=tot[y];
			
			if(tot[y]>tot[mx[x]])sx[x]=mx[x],mx[x]=y;
			else if(tot[y]>tot[sx[x]])sx[x]=y;
			
			int c=tot[y]<=li?y:mq[y];
			if(tot[mq[x]]<tot[c])mq[x]=c;
		}
	}
	if(tot[x]<=li)mq[x]=x;
}
int as[maxn];
void changert(int x,int fr,int p)
{
	int cc=(tot[mx[x]]>li)+(tot[sx[x]]>li)+(tot[fr]>li);
	if(cc==0)as[x]=1;
	else if(cc>1);
	else
	{
		if(tot[mx[x]]>li)
			as[x]=(tot[mx[x]]-tot[mq[x]])<=li;
		else
			as[x]=(tot[fr]-tot[p])<=li;
	}
	
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y!=fr)
		{
			int pp=p;
			tot[x]-=tot[y];
			tot[y]+=tot[x];
			
			if(tot[x]<=li&&tot[x]>tot[p])p=x;
			int aa=S::findmax(1,1,n,L[x],id[y]-1),bb=S::findmax(1,1,n,id[y]+1,R[x]);
			if(tot[p]<tot[aa])p=aa;
			if(tot[p]<tot[bb])p=bb;
			
			changert(y,x,p);
			tot[y]-=tot[x];
			tot[x]+=tot[y];
			p=pp;
		}
	}
}
 
int main()
{
	int x,y;
	scanf("%d",&n);li=n/2;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		ins(x,y),ins(y,x);
	}
	dfs(1,0);
	S::bt(1,1,n);
	
	changert(1,0,0);
	for(int i=1;i<n;i++)printf("%d ",as[i]);
	printf("%d\n",as[n]);
	
	return 0;
E. Student’s Camp

题意:给一个n行m列的图,吹k次风,假如一个格子左或右没有格子就有p的概率被吹烂,求最后上下联通的概率。 n ≤ 1500 n\leq1500 n1500

对于吹烂而言,它是只关于行的,列之间不会相互影响,我们可以预处理出 s [ i ] s[i] s[i]表示k轮中吹烂恰好i个格子的概率。

g [ t ] [ l ] [ r ] g[t][l][r] g[t][l][r]表示当前为第t行,没被吹烂的区间,为了联通上下行必须有交。则有:

g [ t ] [ l ] [ r ] = s [ l − 1 ] ∗ s [ m − r ] ∗ ∑ ∑ ( [ l , r ] ∩ [ i , j ] ≠ ∅ ) g [ t − 1 ] [ i ] [ j ] g[t][l][r]=s[l-1]*s[m-r]*\sum\sum_{([l,r]\cap[i,j]\neq\emptyset)} g[t-1][i][j] g[t][l][r]=s[l1]s[mr]([l,r][i,j]̸=)g[t1][i][j]

这是一个 O ( n 5 ) O(n^5) O(n5)的做法,但不难想到(至少我都想到了)利用容斥原理弄一个二维的前缀和优化

f [ t ] [ l ] [ r ] = ∑ i = l r ∑ j = i r g [ t ] [ i ] [ j ] f[t][l][r]=\sum^r_{i=l}\sum^r_{j=i}g[t][i][j] f[t][l][r]=i=lrj=irg[t][i][j],其现实意义就是吹烂的区间是 [ l , r ] [l,r] [l,r]的子集

g [ t ] [ l ] [ r ] = s [ l − 1 ] ∗ s [ m − r ] ∗ ( f [ t − 1 ] [ 1 ] [ m ] − f [ t − 1 ] [ 1 ] [ l − 1 ] − f [ t − 1 ] [ r + 1 ] [ m ] ) g[t][l][r]=s[l-1]*s[m-r]*(f[t-1][1][m]-f[t-1][1][l-1]-f[t-1][r+1][m]) g[t][l][r]=s[l1]s[mr](f[t1][1][m]f[t1][1][l1]f[t1][r+1][m])

后面那坨意思是没被打穿的总概率减去和当前行没交集的情况的概率

对于f有 f [ t ] [ l ] [ r ] = g [ t ] [ l ] [ r ] + f [ t ] [ l + 1 ] [ r ] + f [ t ] [ l ] [ r − 1 ] − f [ t ] [ l + 1 ] [ r − 1 ] f[t][l][r]=g[t][l][r]+f[t][l+1][r]+f[t][l][r-1]-f[t][l+1][r-1] f[t][l][r]=g[t][l][r]+f[t][l+1][r]+f[t][l][r1]f[t][l+1][r1]

那么就能够做到 O ( n 3 ) O(n^3) O(n3)了,并且此时 a n s = f [ n ] [ 1 ] [ m ] ans=f[n][1][m] ans=f[n][1][m]

为了做到 O ( n 2 ) O(n^2) O(n2)我们必须把状态压下来,保证思路清晰,不难发现上述柿子中f均为前/后缀和的形式,改设:

s l [ t ] [ r ] = f [ t ] [ 1 ] [ r ] = ∑ i = 1 r ∑ j = i r g [ t ] [ i ] [ j ] sl[t][r]=f[t][1][r]=\sum^r_{i=1}\sum^r_{j=i}g[t][i][j] sl[t][r]=f[t][1][r]=i=1rj=irg[t][i][j]

s r [ t ] [ l ] = f [ t ] [ l ] [ m ] = ∑ i = l m ∑ j = i m g [ t ] [ i ] [ j ] sr[t][l]=f[t][l][m]=\sum^m_{i=l}\sum^m_{j=i}g[t][i][j] sr[t][l]=f[t][l][m]=i=lmj=img[t][i][j]

g [ t ] [ l ] [ r ] = s [ l − 1 ] ∗ s [ m − r ] ∗ ( s l [ t − 1 ] [ m ] − s l [ t − 1 ] [ l − 1 ] − s r [ t − 1 ] [ r + 1 ] ) g[t][l][r]=s[l-1]*s[m-r]*(sl[t-1][m]-sl[t-1][l-1]-sr[t-1][r+1]) g[t][l][r]=s[l1]s[mr](sl[t1][m]sl[t1][l1]sr[t1][r+1])

我们用sl来分析,完了sr镜像就好

从现实意义入手,固定右端点r,令 u [ t ] [ r ] = ∑ l = 1 r g [ t ] [ l ] [ r ] u[t][r]=\sum^r_{l=1}g[t][l][r] u[t][r]=l=1rg[t][l][r],则 s l [ t ] [ r ] = ∑ i = 1 r u [ t ] [ i ] sl[t][r]=\sum^r_{i=1}u[t][i] sl[t][r]=i=1ru[t][i],只要能够快速算出u,sl也能够出来, a n s = s l [ n ] [ m ] ans=sl[n][m] ans=sl[n][m]就完成了。把上面关于 g [ t ] [ l ] [ r ] g[t][l][r] g[t][l][r]的等式代入

u [ t ] [ r ] = ∑ l = 1 r s [ l − 1 ] ∗ s [ m − r ] ∗ ( s l [ t − 1 ] [ m ] − s l [ t − 1 ] [ l − 1 ] − s r [ t − 1 ] [ r + 1 ] ) u[t][r]=\sum^r_{l=1}s[l-1]*s[m-r]*(sl[t-1][m]-sl[t-1][l-1]-sr[t-1][r+1]) u[t][r]=l=1rs[l1]s[mr](sl[t1][m]sl[t1][l1]sr[t1][r+1])

= s [ m − r ] ∗ s l [ t − 1 ] [ m ] ∗ ∑ l = 1 r s [ l − 1 ] − =s[m-r]*sl[t-1][m]*\sum^r_{l=1}s[l-1]- =s[mr]sl[t1][m]l=1rs[l1] s [ m − r ] ∗ ∑ l = 1 r s [ l − 1 ] ∗ s l [ t − 1 ] [ l − 1 ] − s[m-r]*\sum^r_{l=1}s[l-1]*sl[t-1][l-1]- s[mr]l=1rs[l1]sl[t1][l1]

s [ m − r ] ∗ s r [ t − 1 ] [ r + 1 ] ∗ ∑ l = 1 r s [ l − 1 ] s[m-r]*sr[t-1][r+1]*\sum^r_{l=1}s[l-1] s[mr]sr[t1][r+1]l=1rs[l1]

弄两个变量表示 ∑ l = 1 r s [ l − 1 ] \sum^r_{l=1}s[l-1] l=1rs[l1] ∑ l = 1 r s [ l − 1 ] ∗ s l [ t − 1 ] [ l − 1 ] \sum^r_{l=1}s[l-1]*sl[t-1][l-1] l=1rs[l1]sl[t1][l1]就好了

同理令 v [ t ] [ l ] = ∑ r = l m g [ t ] [ l ] [ r ] v[t][l]=\sum^m_{r=l}g[t][l][r] v[t][l]=r=lmg[t][l][r],则 s r [ t ] [ l ] = ∑ i = l m v [ t ] [ i ] sr[t][l]=\sum^m_{i=l}v[t][i] sr[t][l]=i=lmv[t][i]

v [ t ] [ l ] = ∑ r = l m s [ l − 1 ] ∗ s [ m − r ] ∗ ( s l [ t − 1 ] [ m ] − s l [ t − 1 ] [ l − 1 ] − s r [ t − 1 ] [ r + 1 ] ) v[t][l]=\sum^m_{r=l}s[l-1]*s[m-r]*(sl[t-1][m]-sl[t-1][l-1]-sr[t-1][r+1]) v[t][l]=r=lms[l1]s[mr](sl[t1][m]sl[t1][l1]sr[t1][r+1])

= s [ l − 1 ] ∗ s l [ t − 1 ] [ m ] ∗ ∑ r = l m s [ m − r ] − s [ l − 1 ] ∗ s l [ t − 1 ] [ l − 1 ] ∗ ∑ r = l m s [ m − r ] − =s[l-1]*sl[t-1][m]*\sum^m_{r=l}s[m-r]-s[l-1]*sl[t-1][l-1]*\sum^m_{r=l}s[m-r]- =s[l1]sl[t1][m]r=lms[mr]s[l1]sl[t1][l1]r=lms[mr]

s [ l − 1 ] ∗ ∑ r = l m s [ m − r ] ∗ s r [ t − 1 ] [ r + 1 ] s[l-1]*\sum^m_{r=l}s[m-r]*sr[t-1][r+1] s[l1]r=lms[mr]sr[t1][r+1]

反着来同样上两个变量即可

O ( n 3 ) O(n^3) O(n3)

	f[0][1][m]=1;
	for(int i=1;i<=n;i++)
		for(int l=m;l>=1;l--)
			for(int r=l;r<=m;r++)
			{
				f[i][l][r]=mu( re(f[i-1][1][m],ad(f[i-1][1][l-1],f[i-1][r+1][m])) , mu(s[l-1],s[m-r]) );
				f[i][l][r]=re(ad(f[i][l][r],ad(f[i][l+1][r],f[i][l][r-1])),f[i][l+1][r-1]);
			}

O ( n 2 ) O(n^2) O(n2)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=1500+_;
const int maxK=1e5+_;
const int mod=1e9+7;
inline int ad(int x,int y){return (mod-y<=x)?x-mod+y:x+y;}
inline int re(int x,int y){return (x<y)?x-y+mod:x-y;}
inline int mu(int x,int y){return (LL)x*y%mod;}
inline int qp(int x,int y){int r=1;while(y){if(y&1)r=mu(r,x);x=mu(x,x);y>>=1;}return r;}
inline int dv(int x,int y){return mu(x,qp(y,mod-2));}
int n,m,p,K;
 
int fac[maxK],fac_inv[maxK],tt[maxK],s[maxn];
int C(int n,int m){return mu(mu(fac[n],fac_inv[m]),fac_inv[n-m]);}
void yu()
{
	fac[0]=1;for(int i=1;i<maxK;i++)fac[i]=mu(fac[i-1],i);
	fac_inv[maxK-1]=qp(fac[maxK-1],mod-2);
	for(int i=maxK-2;i>=0;i--)fac_inv[i]=mu(fac_inv[i+1],i+1);
	int li=min(K,maxn-1);
	for(int i=0;i<=li;i++)s[i]=mu(mu(C(K,i),qp(p,i)),qp(re(1,p),K-i));
}
 
int sl[maxn][maxn],sr[maxn][maxn],u[maxn][maxn],v[maxn][maxn];
int main()
{
	int AA,BB;
	scanf("%d%d%d%d%d",&n,&m,&AA,&BB,&K);p=dv(AA,BB); yu();
	sl[0][m]=1,sr[0][1]=1;
	for(int t=1;t<=n;t++)
	{
		AA=0,BB=0;
		for(int r=1;r<=m;r++)
		{
			AA=ad(AA,s[r-1]),BB=ad(BB,mu(s[r-1],sl[t-1][r-1]));
			u[t][r]=mu(mu(s[m-r],sl[t-1][m]),AA);
			u[t][r]=re(u[t][r],mu(s[m-r],BB));
			u[t][r]=re(u[t][r],mu(mu(s[m-r],sr[t-1][r+1]),AA));
			sl[t][r]=ad(sl[t][r-1],u[t][r]);
		}
		AA=0,BB=0;
		for(int l=m;l>=1;l--)
		{
			AA=ad(AA,s[m-l]),BB=ad(BB,mu(s[m-l],sr[t-1][l+1]));
			v[t][l]=mu(mu(s[l-1],sl[t-1][m]),AA);
			v[t][l]=re(v[t][l],mu(mu(s[l-1],sl[t-1][l-1]),AA));
			v[t][l]=re(v[t][l],mu(s[l-1],BB));
			sr[t][l]=ad(sr[t][l+1],v[t][l]);
		}
	}
	printf("%d\n",sl[n][m]);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值