CodeTON Round 7 (Div. 1 + Div. 2) 解题报告

CodeTON Round 7 (Div. 1 + Div. 2)

Contest Link

广告:本场比赛博主使用了 CCH 完成,体验很好,推荐高 rating 用户使用(低 rating 受 cloudflare 影响很大)。

A. Jagged Swaps

\(\text{Status: \color{green}+\color{black} 00:03}\)

结论:输出 YES 当且仅当 \(a_1=1\)

证明:

如果 \(a_1\ne1\),而 \(a_1\) 不可能参与交换,所以 \(1\) 一定无法换到 \(a_1\)

如果 \(a_1=1\),那么如果序列未排好序,一定存在一个位置可以交换,减少一个逆序对,从而可以排好序。

时间复杂度:\(\Theta(n)\)

const int N=15;
int n,a[N];
void Solve(int CASE)
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	puts(a[1]==1?"YES":"NO");
}

B. AB Flipping

\(\text{Status: \color{green}+\color{black} 00:09}\)

操作其实就是交换一对 \(\texttt{A}\)\(\texttt{B}\)

开头的一堆 \(\texttt{B}\) 和结尾的一堆 \(\texttt{A}\) 都是换不了的,我们把这些没用的都删掉。下面设 \(s_1=\texttt{A},s_n=\texttt{B}\)

下面证明此时一定可以交换 \(n-1\) 次。

证:考虑何时在位置 \(n-1\) 进行操作。

如果 \(s_{n-1}=\texttt{A}\),那么直接交换 \(s_{n-1}\)\(s_n\),再把交换后的 \(s_n\) 扔掉,就变成了一个长度为 \(n-1\) 的,满足上述条件的字符串。

如果 \(s_{n-1}=\texttt{B}\),那么 \(s_{1\ldots n-1}\) 满足上述条件,那么先对 \(s_{1\ldots n-1}\) 进行操作。可以证明此时有 \(s_{n-1}=\texttt{A}\),再对 \(n-1\) 进行一次操作即可。

边界情况:\(n=2\),此时只有 \(s=\texttt{AB}\),此时交换即可。

由数学归纳法原理,命题得证。

时间复杂度:\(\Theta(n)\)

int n;
string s;
void Solve(int CASE)
{
	cin>>n>>s;
	int l=0,r=n-1;
	for(;l<n&&s[l]=='B';l++);
	for(;r&&s[r]=='A';r--);
	cout<<max(r-l,0)<<endl;
}

C. Matching Arrays

\(\text{Status: \color{green}+ \color{black}00:26}\)

我们想要让 \(a\) 中其中 \(x\) 个元素尽量比 \(b\) 中的大,而其余元素尽量比 \(b\) 中的小。

那我们可以用 \(a\)\(x\) 个最大的去按顺序干 \(b\)\(x\) 个最小的,反之亦然。

然后随便 check 一下即可。

时间复杂度:\(\Theta(n\log n)\),瓶颈在于排序。当然你想用桶排也没人拦你

就这 b 玩意我想 + 打了将近 20 分钟。

const int N=200005;
int n,x,b[N],bb[N];
pair<PII,int> a[N];
void Solve(int CASE)
{
	cin>>n>>x;
	for(int i=1;i<=n;i++)cin>>a[i].fi.fi,a[i].fi.se=i;
	for(int i=1;i<=n;i++)cin>>b[i];
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	for(int i=1;i<=n;i++)
		bb[(i-x+n-1)%n+1]=b[i];
	memcpy(b+1,bb+1,n<<2);
	int cnt=0;
	for(int i=1;i<=n;i++)cnt+=a[i].fi.fi>b[i];
	if(cnt!=x)put_ret("NO");
	cout<<"YES\n";
	for(int i=1;i<=n;i++)a[i].se=b[i];
	sort(a+1,a+n+1,[](auto x,auto y){return x.fi.se<y.fi.se;});
	for(int i=1;i<=n;i++)cout<<a[i].se<<sp_el(i,n);
}

D. Ones and Twos

\(\text{Status: \color{green}+1 \color{black}00:47}\)

\(f_{l,r}=a_{l}+a_{l+1}+\ldots+a_r\)

先看查询操作。

考虑先搞出来是否存在一段前缀的和等于 \(s\)

如果存在答案就是 \(\texttt{YES}\) 了。那什么时候答案是 \(\texttt{NO}\) 呢?

下面考虑无解的情况。

首先,如果整个数组的元素加起来还不到 \(s\),那直接就无解了。

否则,设 \(p\) 满足 \(f_{1,p}<s,f_{1,p+1}>s\)

由于 \(a_i\) 只能取 \(1\)\(2\),所以只能 \(f_{1,p}=s-1,f_{1,p+1}=s+1\)。这说明 \(a_{p+1}\) 一定为 \(2\)

然后观察到 \(f_{2,p+1}=f_{1,p+1}-a_1=s+1-a_1\ne s\),所以 \(a_1\) 也为 \(2\)

又由于 \(f_{2,p+2}=f_{2,p+1}+a_{p+2}=s-1+a_{p+2}\),所以 \(a_{p+2}=2\)

以此类推。那么是不是要求 \(a\) 数组全是 \(2\) 呢?

不是的。当 \(a=[2,1,2],s=4\) 时是无解的。

可以发现,当右边顶到 \(n\) 的时候就无法滚下去了。

再稍微推一下,可以发现它只要求 \(a_1,a_2,\ldots,a_{n-p},a_{p+1},\ldots,a_n\) 都是 \(2\) 就无解。

于是做完了。\(1\) 的位置可以 set 维护,找 \(p\) 的话直接二分即可。由于带单点修改,树状数组拍上去即可。

时间复杂度:\(\Theta(n\log^2n)\),如果用倍增之类的可以降到 \(\Theta(n\log n)\)

const int N=100005;
int n,q;
int a[N];
set<int>s1;
int c[N];
void add(int x,int v){while(x<=n)c[x]+=v,x+=x&-x;}
int query(int x){int r=0;while(x)r+=c[x],x-=x&-x;return r;}
void Solve(int CASE)
{
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(a[i]==1)s1.insert(i);
		add(i,a[i]);
	}
	while(q--)
	{
		int op,x;
		cin>>op>>x;
		if(op==1)
		{
			int l=1,r=n;
			while(l<=r)
			{
				int mid=l+r>>1;
				if(query(mid)>x)r=mid-1;
				else l=mid+1;
			}//r
			if(query(r)==x){puts("YES");continue;}
			else if(r==n&&query(r)<x-1){puts("NO");continue;}
			else puts(s1.empty()||*s1.begin()>n-r&&*s1.rbegin()<=r?
				"NO":"YES");
		}
		else
		{
			int v;cin>>v;
			if(a[x]==v)continue;
			a[x]=v;
			add(x,v==2?1:-1);
			if(v==2)s1.erase(x);
			else s1.insert(x);
		}
	}
}

E. Permutation Sorting

\(\text{Status: \color{green}+\color{black} 01:13}\)

考虑位置 \(i\),这个数肯定要转到 \(a_i\) 的位置。

正常情况下(一次走一步),那么就要走 \((a_i-i+n)\bmod n\) 步。

不妨强制把环断成长一倍的链。

然后把 \([i,a_i]\) 转成模 \(n\) 意义下的线段(注意有一些段会变成两个,此时计算是算任意一段,但是算贡献的时候两段都要算上)。

例如对于样例 \(1\)

\[a_1=3\to [1,3]\text{ and }[6,8] \]

\[a_2=2\to [2,2]\text{ and }[7,7] \]

\[a_3=4\to [3,4]\text{ and }[8,9] \]

\[a_4=1\to [4,6] \]

\[a_5=5\to [5,5]\text{ and }[10,10] \]

我们可以发现,位置 \(j\) 的数字对位置 \(i\) 的数字产生 \(-1\) 的贡献,当且仅当:

存在一个 \((i,a_i)\) 产生的线段 \(X\) 与一个 \((j,a_j)\) 产生的线段 \(Y\),满足 \(Y\subseteq X\)

例如,\([2,2]\subseteq[1,3]\),所以 \(ans_{a_1}\) 要减一。

然后树状数组从后往前维护一下贡献即可。具体我也不会讲了。

时间复杂度:\(\Theta(n\log n)\)

const int N=1000005;
int n,a[N],ans[N];
int c[N<<1];
void add(int x,int v){while(x<=n<<1)c[x]+=v,x+=x&-x;}
int query(int x){int r=0;while(x)r+=c[x],x^=x&-x;return r;}
void Solve(int CASE)
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=n;i;i--)
		if(a[i]>=i)add(a[i]+n,1);
	for(int i=n;i;i--)
	{
		int to=a[i]<i?a[i]+n:a[i];
		ans[a[i]]=(to-i)-(query(to)-query(i));
		add(to,1);
	}
	for(int i=1;i<=n;i++)cout<<ans[i]<<sp_el(i,n);
}

F. Bracket Xoring

\(\text{Status: \color{black}NaN}\)

暂时不会。

G. Pepe Racing

\(\text{Status: \color{black}NaN}\)

暂时不会。

H. Cyclic Hamming

\(\text{Status: \color{black}NaN}\)

暂时不会。

posted @ 2023-11-26 15:19  No_Play_Yes_Splay  阅读(47)  评论(0编辑  收藏  举报