CodeTON Round 3 (Div. 1 + Div. 2, Rated, Prizes!)

Preface

早自习后的两节空课不想回寝室了就在教室补题,发现没睡醒的时候真是究极混沌状态

憋了半天勉强写了ABC,D是后来晚自习补上的,E看错题目了,F不会看题解会的,GH不打算看


A. Indirect Sort

找性质题,不难发现若\(a_1=1\),则我们可以对\([2,n]\)中的元素进行交换操作,肯定可以做到有序

否则若\(a_1\ne 1\),则我们不能把在后面的\(1\)变大或是交换到\(a_1\)的位置上,因此肯定不合法

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=15;
int t,n,a[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
		puts(a[1]==1?"Yes":"No");
	}
	return 0;
}

B. Maximum Substring

感觉思维难度比T1还低啊

不难发现若\(x>0\and y>0\)的话只要考虑整个串即可,否则找出最长的连续颜色段,比较一下即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,c[2]; char s[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%s",&n,s+1),c[0]=c[1]=0,i=1;i<=n;++i) ++c[s[i]-'0'];
		int mx=0,lst=1; for (i=2;i<=n;++i) if (s[i]!=s[i-1]) mx=max(mx,i-lst),lst=i;
		mx=max(mx,n-lst+1); printf("%lld\n",max(1LL*mx*mx,1LL*c[0]*c[1]));
	}
	return 0;
}

C. Complementary XOR

我发现只要不是比赛的时候我猜结论就准的一批,真到比赛的时候屁都猜不出来

刚开始想了半天的构造前后缀的方法,但是不能保证操作次数小于\(n+5\)

后来随便Rush了一发直接把\(a\)先全部置为\(0\)然后看\(b\)是不是全为\(0\)\(1\),没想到直接过了

其实原理很简单,我们考虑倒着做,从初始两个串都是\(0\)开始做变换

不难发现奇数次操作后\(a_i,b_i\)一定不同,而偶数次操作后\(a_i,b_i\)一定相同

因此只有初始时所有\(a_i,b_i\)都不同或者都相同才有解,有解的情况一定可以用上面的方法求出一组解

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,l[N],r[N],cnt,dlt[N]; char a[N],b[N];
inline void flip(CI L,CI R)
{
	if (L>R) return; ++dlt[L]; --dlt[R+1];
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%s%s",&n,a+1,b+1),i=0;i<=n+1;++i) dlt[i]=0;
		for (cnt=0,i=1;i<=n;++i) if (a[i]=='1')
		{
			for (j=i;j<=n&&a[j]=='1';++j);
			l[++cnt]=i; r[cnt]=j-1; flip(1,i-1); flip(j,n); i=j;
		}
		for (i=1;i<=n;++i) if ((dlt[i]+=dlt[i-1])&1) b[i]=b[i]=='0'?'1':'0';
		bool flag=1; for (i=2;i<=n&&flag;++i) if (b[i]!=b[1]) flag=0;
		puts(flag?"YES":"NO"); if (!flag) continue;
		if (b[1]=='1') l[cnt+1]=1,r[cnt+1]=n,l[cnt+2]=r[cnt+2]=1,l[cnt+3]=2,r[cnt+3]=n,cnt+=3;
		for (printf("%d\n",cnt),i=1;i<=cnt;++i) printf("%d %d\n",l[i],r[i]);
	}
	return 0;
}

D. Count GCD

额很有CF风格的一道题吧,又是码个暴力然后分析复杂度的题

首先我们发现每个位置的情况独立,求出每一位的情况然后乘起来即可

由于\(\gcd(b_1,b_2,\cdots,b_{i-1})=a_{i-1}\),因此要求的其实就是\(\gcd(a_{i-1},b_i)=a_i\)的方案数

先去除无解的情况,稍加转化就是\(\gcd(\frac{a_{i-1}}{a_i},\frac{b_i}{a_i})=1\)的方案数,即在\([1,\lfloor\frac{m}{a_i}\rfloor]\)中与\(\frac{a_{i-1}}{a_i}\)互质的数的个数

这个其实是个经典问题了,我们把\(\frac{a_{i-1}}{a_i}\)质因数分解,然后用容斥算方案数即可

复杂度看似爆炸其实由于\(10^9\)范围内质因数分解出最多只有\(10\)个,因此这部分复杂度是\(2^{10}\)级别的

同时我们观察到在有解的情况下\(a_i\)一定是单调不增的,且若\(a_i\ne a_{i-1}\),则\(a_i\)在除去一个因子后衰减的次数是\(\log a_i\)量级的

\(a_{i-1}=a_i\)的情形直接用map顺便记录下即可,复杂度大概是\(O(n\log a_i\times 2^{10}\times 10)\)

#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=998244353;
int t,n,m,a[N],ans; map <int,int> rst; vector <int> p,rp;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j,k; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
		p.clear(); rst.clear(); int t=a[1];
		for (i=2;i*i<=t;++i) if (t%i==0)
		{
			p.push_back(i); while (t%i==0) t/=i;
		}
		if (t>1) p.push_back(t); for (ans=1,i=2;i<=n;++i)
		{
			if (a[i-1]%a[i]) { ans=0; break; }
			if (a[i-1]==a[i]&&rst.count(a[i])) { ans=1LL*ans*rst[a[i]]%mod; continue; }
			int lim=m/a[i],t=a[i-1]/a[i]; rp.clear();
			for (int x:p) if (t%x==0) rp.push_back(x);
			int ret=0; for (j=0;j<(1<<rp.size());++j)
			{
				int cnt=0,mul=1; for (k=0;k<rp.size();++k)
				if ((j>>k)&1) ++cnt,mul*=rp[k];
				ret+=(cnt&1?-1:1)*lim/mul;
			}
			ans=1LL*ans*ret%mod; if (a[i-1]==a[i]) rst[a[i]]=ret;
		}
		printf("%d\n",ans);
	}
	return 0;
}

E. Bracket Cost

刚开始的时候看错题目了,以为那个移位操作只能对整个串进行操作,没想到是对子串

首先一个经典的trick,把括号序列的前缀和\(pfx\)求出来,左括号看作1,右括号看作-1

然后考虑一个区间\([l,r]\)的答案,不难发现首先要把左右括号的个数补成一样

接下来考虑位移操作,不难发现我们都是可以把位于最后的左括号换到最前面

因此整理一下答案就是\(\max(f_{l-1},f_r)-\min_{l-1\le i\le r} f_i\),不难发现两边分开计算比较简单

前面的用树状数组维护下,后面的用单调栈维护下即可

#include<cstdio>
#define RI register int
#define CI const int&
const int N=200005;
int t,n,lim,pfx[N],L[N],R[N],stk[N],top; char s[N]; long long ans;
#define lowbit(x) (x&-x)
class TreeArray1 //count the number of x that x<=i
{
	private:
		int bit[N<<1];
	public:
		inline void init(CI n)
		{
			for (RI i=1;i<=n;++i) bit[i]=0;
		}
		inline void add(RI x)
		{
			for (;x<=lim;x+=lowbit(x)) ++bit[x];
		}
		inline int get(RI x,int ret=0)
		{
			for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
		}
}T1;
class TreeArray2 //count the sum of x that x>=i
{
	private:
		long long bit[N<<1];
	public:
		inline void init(CI n)
		{
			for (RI i=1;i<=n;++i) bit[i]=0;
		}
		inline void add(RI x,CI y)
		{
			for (;x;x-=lowbit(x)) bit[x]+=y;
		}
		inline long long get(RI x,long long ret=0)
		{
			for (;x<=lim;x+=lowbit(x)) ret+=bit[x]; return ret;
		}
}T2;
#undef lowbit
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%s",&n,s+1); ans=0; lim=n+n+1; T1.init(lim); T2.init(lim);
		for (pfx[0]=0,i=1;i<=n;++i) pfx[i]=pfx[i-1]+(s[i]=='('?1:-1);
		for (i=0;i<=n;++i) pfx[i]+=n+1; for (i=0;i<=n;++i)
		ans+=1LL*T1.get(pfx[i])*pfx[i]+T2.get(pfx[i]+1),T1.add(pfx[i]),T2.add(pfx[i],pfx[i]);
		for (stk[top=i=0]=-1;i<=n;++i)
		{
			while (top&&pfx[stk[top]]>pfx[i]) --top;
			L[i]=stk[top]; stk[++top]=i;
		}
		for (stk[top=0]=n+1,i=n;~i;--i)
		{
			while (top&&pfx[stk[top]]>=pfx[i]) --top;
			R[i]=stk[top]; stk[++top]=i;
		}
		for (i=0;i<=n;++i) ans-=1LL*pfx[i]*(1LL*(i-L[i])*(R[i]-i)-1);
		printf("%lld\n",ans);
	}
	return 0;
}

F. Majority

STO CXR ORZ

妈的看了半天Tutorial发现陈指导写了这题的博客,赶紧去膜一发

首先我们发现对于任意一个串,我们发现在操作了一定次数后它能变成的状态一定是唯一的

考虑若这个串不是全\(1\),且假设两个端点都不是\(0\),那么我们发现这个串一定变成了\(2k+1\)个连续颜色段

其中奇数下标的段都是\(1\),偶数下标的段都是\(0\),同时每一个\(0\)段左右两侧的\(1\)段的长度之和均小于这个\(0\)

我们不妨这样设计状态,令\(f_{i,j}\)表示长度为\(i\)的串,最终状态下结尾的\(1\)的长度为\(j\)的方案数

不难发现只有\(f_{i,i}\)是合法状态,而\(f_{i,j}(j<i)\)都是不合法的,而且由容斥我们有\(f_{i,i}=2^{i-2}-\sum_{j=1}^{i-1} f_{i,j}\)(特别地,\(f_{1,1}=1\)

考虑对于\(j<i\)\(f_{i,j}\)的转移,考虑枚举前面一段\(0\)的长度\(k\),设再前面一段\(1\)的长度为\(x\),则有:

\[f_{i,j}=\sum_{k,x} f_{i-j-k,x}\times f_{j,j}\ \ (x+j<k)\\ \Leftrightarrow f_{i,j}=\sum_{p+q<i-2\times j} f_{p,q} \times f_{j,j}\ \ ((i-j+k)+x<i-2\times j) \]

\(g_{t}=\sum_{i+j\le t} f_{i,j}\),则可以\(O(1)\)转移,总复杂度\(O(n^2)\)

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=5005;
int n,mod,f[N][N],g[N],pw;
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j; for (scanf("%d%d",&n,&mod),i=2;i<=n;++i) g[i]=1;
	for (f[1][1]=pw=1,i=2;i<=n;++i)
	{
		for (j=1;j<i;++j) if (i-2*j>0) f[i][j]=1LL*g[i-2*j-1]*f[j][j]%mod;
		for (f[i][i]=pw,inc(pw,pw),j=1;j<i;++j) inc(f[i][i],mod-f[i][j]);
		int sum=0; for (j=1;i+j<=n;++j) inc(sum,f[i][j]),inc(g[i+j],sum);
	}
	return printf("%d",f[n][n]),0;
}

Postscript

妈的补题的时候写什么都是一遍过,比赛的时候写什么都要挂

posted @ 2022-11-11 22:15  空気力学の詩  阅读(177)  评论(0编辑  收藏  举报