【Codeforces】1129 Round #542 [Alex Lopashev Thanks-Round] (Div. 1) A-E题解

传送门:CF1129

手速变慢了…代码熟练度不够


A1&2. Toy Train

g [ x ] g[x] g[x]存起点为 a i a_i ai的所有 b i b_i bi(按 d i s ( a i , b i ) dis(a_i,b_i) dis(ai,bi)排序)。

m x = m a x ( g [ x ] . s i z e ( ) ) mx=max(g[x].size()) mx=max(g[x].size()),则对于任意起点,前 m x − 1 mx-1 mx1圈一定是要走的,只是最后一圈不一样。,枚举起点 O ( n 2 ) O(n^2) O(n2)跑一遍最后一圈即可。(有一些细节)

#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define sc second
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef double db;
const int N=20005;

int n,m,cnt,mx,cot,pos[5010];ll ans,res;
vector<int>g[5010],rep[5010];
int del[5010];

inline int dis(int x,int y)
{
	if(y>=x) return y-x;
	return y-x+n;
}

inline int add(int x,int y)
{
	x+=y;return x>n?x-n:x;
}

inline void cal(int st)
{
	int i,nw=0;cot=0;ll bs=0;
	memset(del,0,sizeof(del));
	for(i=1;i<=n;++i){
		rep[i].clear();pos[i]=0;
		if(g[i].size()+1<mx) continue;
		if(g[i].size()==mx){
			if(mx>1 && dis(st,i)>dis(st,add(i,g[i][1])) && add(i,g[i][1])!=st){
				nw++;del[add(i,g[i][1])]++;
			}
		    rep[i].pb(g[i][0]),cot++;
	    }else{
	    	if(mx>1 && dis(st,i)>dis(st,add(i,g[i][0])) && add(i,g[i][0])!=st)
			nw++,del[add(i,g[i][0])]++;
		}
	}
	for(i=st;;i= i==n?1:(i+1)){
		if(pos[i]<rep[i].size()){
			nw++;del[add(i,rep[i][pos[i]])]++;
			pos[i]++;cot--;
		}
		nw-=del[i];del[i]=0;
		if((!nw)&&(!cot)){
			printf("%I64d ",bs+ans);return;
		}
		bs++;
	}
}

int main(){
	int i,j,a,b;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i){
		scanf("%d%d",&a,&b);
		g[a].pb(dis(a,b));
	}
	for(i=1;i<=n;++i) sort(g[i].begin(),g[i].end());
	mx=0;
	for(i=1;i<=n;++i) mx=max(mx,(int)g[i].size());
	ans+=(ll)(mx-1)*n;
	for(int x=1;x<=n;++x) cal(x);
	return 0;
}

B.Wrong Answer

这题想了很久,因为没法证明构造是对的,然而AC了…

考虑构造一个长度为 2000 2000 2000的答案,分成三部分:
一段前缀0+一个’-1’+全为正的后缀(设和为 S S S,长度为 l l l)。

则Alice的答案是 S ⋅ l S·l Sl,实际答案为 ( S − 1 ) n (S-1)n (S1)n

S n − n − S l = k → S ( n − l ) = k + n Sn-n-Sl=k\to S(n-l)=k+n SnnSl=kS(nl)=k+n

枚举 l l l判断即可。

正确性玄学?


C.Morse Code

构造 S A M SAM SAM,对于每个结点维护 t i m i tim_i timi表示该结点right集合中最前的位置, d p i dp_i dpi表示从根走到当前结点的所有字符串能构成的摩斯密码的方案数。

拓扑排序 d p dp dp,每次 O ( 2 4 ) O(2^4) O(24)暴力转移。

#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define sc second
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef double db;
const int N=3005,mod=1e9+7;

int n,cnt=1,last,cur=1,dis[N<<1];
int dp[N<<1],tim[N<<1],ans[N];
ll sum;

inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
int ch[N<<1][2];

struct bk{
    int fa,size;
}t[N<<1];

inline void insert(int a,int d)
{
    last=cur;cur=++cnt;dis[cur]=d;tim[cur]=d;
    int p=last;
    for(;p && !ch[p][a];p=t[p].fa) ch[p][a]=cur;
    if(!p) t[cur].fa=1;else{
        int q=ch[p][a];
        if(dis[q]==dis[p]+1) t[cur].fa=q;else{
            int nt=++cnt;tim[cnt]=tim[q];
			dis[nt]=dis[p]+1;
            memcpy(ch[nt],ch[q],sizeof(ch[q]));
            t[nt].fa=t[q].fa;t[q].fa=nt;t[cur].fa=nt;
            for(;ch[p][a]==q;p=t[p].fa) ch[p][a]=nt;
        }
    }
    t[cur].size=1;
}

int dd[N<<1];
queue<int>que;

int main(){
	int i,j,k,a,b,c,i1,i2,i3,i4,j1,j2,j3,j4;
	scanf("%d",&n);
	for(i=1;i<=n;++i){
		scanf("%d",&a);
		insert(a,i);
	}
	for(i=1;i<=cnt;++i)
	 for(j=0;j<2;++j)
	  if(ch[i][j]) dd[ch[i][j]]++;
	que.push(1);dp[1]=1;
	for(;!que.empty();){
		a=que.front();que.pop();b=dp[a];
		ad(ans[tim[a]],b);
		for(i1=0;i1<2;++i1) if(ch[a][i1]){
			ad(dp[(j1=ch[a][i1])],b);
			for(i2=0;i2<2;++i2) if(ch[j1][i2]){
				ad(dp[(j2=ch[j1][i2])],b);
				for(i3=0;i3<2;++i3) if(ch[j2][i3]){
					ad(dp[(j3=ch[j2][i3])],b);
					for(i4=0;i4<2;++i4) if(ch[j3][i4]){
						ad(dp[ch[j3][i4]],b);
					}
				}
				
			}
		}
		if(ch[ch[ch[ch[a][0]][0]][1]][1])
			dc(dp[ch[ch[ch[ch[a][0]][0]][1]][1]],b);
		if(ch[ch[ch[ch[a][0]][1]][0]][1])
		    dc(dp[ch[ch[ch[ch[a][0]][1]][0]][1]],b);
		if(ch[ch[ch[ch[a][1]][1]][1]][0])
		    dc(dp[ch[ch[ch[ch[a][1]][1]][1]][0]],b);
		if(ch[ch[ch[ch[a][1]][1]][1]][1])
		    dc(dp[ch[ch[ch[ch[a][1]][1]][1]][1]],b);
		for(j=0;j<2;++j) if(ch[a][j]){
		   dd[ch[a][j]]--;if(!dd[ch[a][j]]) que.push(ch[a][j]);
		}
	}
	for(i=1;i<n;++i) ad(ans[i+1],ans[i]);
	for(i=1;i<=n;++i) printf("%d\n",ans[i]);
	return 0;
}

D.Isolation

r = 1 − n r=1-n r=1n扫过去,动态维护 t i t_i ti表示区间 [ i , r ] [i,r] [i,r]中只出现一次的数的个数,每次处理一下 a r a_r ar和前驱之间的值再 d p dp dp一下即可。

复杂度也比较玄学,题解没看懂,似乎是 O ( n n ) O(n\sqrt n) O(nn )的。


E.Legendary Tree

构造题一向比较巧妙:

设根为1。

首先可以 O ( n ) O(n) O(n)求出每个点的子树大小 s z [ i ] sz[i] sz[i](询问1到其他所有点的路径经过点 i i i的个数)。

将点按 s z sz sz升序排序,逐个找到每个点的儿子结点。

设集合 S S S,存储所有未找到父节点的结点,扫到结点 i i i时,暴力二分找到最小的 k k k,满足 1 1 1 s 1 , s 2 , . . . , s k s_1,s_2,...,s_k s1,s2,...,sk的路径经过点 i i i的个数非0, k k k就是 i i i的儿子结点。

询问次数在 n + n log ⁡ n n+n\log n n+nlogn级别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值