Codeforces Round #833 (Div. 2)

Preface

我是大FW,B因为本地调试的时候把数组大小改成200忘记该回去了浪费了很多时间和罚时

C刚开始也没想清楚WA了两发心态爆炸,然后D其实想出了一种做法但是心态崩了就没写了

又是掉分我感觉有点难顶


A. The Ultimate Square

一个SB题给我做的巨蠢,直接输出\(\lceil\frac{n}{2}\rceil\)即可,但是我写的是个什么鬼我也不知道

#include<cstdio>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=10005;
int t,n;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	scanf("%d",&n),printf("%d\n",(int)sqrt(1LL*n/2*(n/2+1)+(n&1?n+1>>1:0)));
	return 0;
}

B. Diverse Substrings

因为数的种类只有10种,根据抽屉原理,一个区间的长度最多为\(100\),因此直接暴力做即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,c[10],ans; char s[N];
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",&n,s+1),ans=0,i=1;i<=n;++i)
		{
			for (j=0;j<10;++j) c[j]=0; int num=0,mx=0;
			for (j=i;j<=n;++j)
			{
				if (!c[s[j]-'0']) ++num; if (++c[s[j]-'0']>mx) mx=c[s[j]-'0'];
				if (mx<=num) ++ans; else if (mx>10) break;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

C. Zero-Sum Prefixes

首先求出原序列的前缀和\(pfx\),考虑被\(0\)分开的每段区间,不难发现这个区间前面的\(0\)改成这个区间里出现次数最多的\(pfx\)即可

但是要注意开头的那个前面没有\(0\)的区间的情况要特别考虑,以及最后留下的一个单独的\(0\)结尾的情况

#include<cstdio>
#include<map>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,x,ans; map <long long,int> c;
inline void calc(int mx=0)
{
	for (auto v:c) mx=max(mx,(v.first?0:1)+v.second); ans+=mx;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; long long pfx=0,lst=0; bool flag=0;
		for (scanf("%d",&n),ans=0,i=1;i<=n;++i)
		{
			scanf("%d",&x);
			if (!x) calc(),c.clear(),c[0]=pfx=0,flag=1; else
			{
				if (!flag)
				{
					if (pfx+=x,!pfx) ++ans;
				} else ++c[pfx+=x];
			}
		}
		calc(); printf("%d\n",ans); c.clear();
	}
	return 0;
}

D. ConstructOR

比赛的时候想了个算法以为是假的就没写,后来发现是对的(虽然我不会证正确性)

就是先考虑\(x\)的后\(30\)位,我们令\(x=a|b\),这样两个数的限制就变成一个了,记\(y=a|b\bmod d\)

接下来考虑前\(30\)位,相当于解方程\(2^{30}\times x'=d-y\pmod d\),直接上扩欧即可,代码我没写

考虑一种科学的做法,我们发现若\(a,b\)中有一个为奇数,而\(d\)为偶数时显然是无解的

否则说明\(a,b,d\)最低位都是\(0\),原问题转化为\(\frac{a}{2},\frac{b}{2},\frac{d}{2}\)之间的问题

因此我们设\(\operatorname{LSB}(x)\)表示\(x\)的二进制表示中最低位的\(1\)出现的位置,则\(\min(\operatorname{LSB}(a),\operatorname{LSB}(b))<\operatorname{LSB}(d)\)时无解

考虑有解的情况,我们从低到高考虑每一个\(x\)\(0\)\(d\)\(1\)的位,把答案加上\(d\times 2^k\)即可(\(k\)为当前位与\(\operatorname{LSB}(d)\)的差值)

#include<cstdio>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
int t,a,b,d;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i; bool flag=1; int lsb;
		for (scanf("%lld%lld%lld",&a,&b,&d),i=0;i<30;++i)
		{
			if ((d>>i)&1) { lsb=i; break; }
			if (((a|b)>>i)&1) { flag=0; break; }
		}
		if (!flag) { puts("-1"); continue; }
		int ans=0; for (i=lsb;i<30;++i)
		if ((((a|b)>>i)&1)&&!((ans>>i)&1)) ans+=d<<(i-lsb);
		printf("%lld\n",ans); 
	}
	return 0;
}

E. Yet Another Array Counting Problem

论你能不能想到笛卡尔树

今天补题的时候刚打开Tutorial结果看到笛卡尔树就知道怎么做了

原来的题目要求就是\(a,b\)的笛卡尔树同构,直接来个DP,设\(f_{i,j}\)表示树上点\(i\)的权值为\(j\)时的方案数

转移的话设\(g_{i,j}=\sum_{k\le j} f_{i,k}\),则\(f_{i,j}=g_{lson_i,j-1}\times g_{rson_i,j}\),注意左子树因为出现在\(i\)前面因此不能等于\(i\),而右子树就可以等于

#include<cstdio>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=1e9+7;
int t,n,m,a[N]; vector <int> f[N];
class Cartesian_tree
{
	private:
		int ch[N][2],stk[N],top;
		#define lc(x) ch[x][0]
		#define rc(x) ch[x][1]
	public:
		int rt;
		inline void build(void)
		{
			RI i; for (top=stk[1]=0,i=1;i<=n;++i)
			{
				lc(i)=rc(i)=0; while (top&&a[i]>a[stk[top]]) --top;
				if (top) lc(i)=rc(stk[top]),rc(stk[top])=i; else lc(i)=stk[1]; stk[++top]=i;
			}
			for (rt=stk[1],i=0;i<=m;++i) f[0][i]=1;
		}
		inline void DP(CI now)
		{
			if (!now) return; DP(lc(now)); DP(rc(now)); for (RI i=1;i<=m;++i)
			f[now][i]=1LL*f[lc(now)][i-1]*f[rc(now)][i]%mod,(f[now][i]+=f[now][i-1])%=mod;
		}
		#undef lc
		#undef rc
}CT;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
		for (i=0;i<=n;++i) for (f[i].resize(m+1),j=0;j<=m;++j) f[i][j]=0;
		CT.build(); CT.DP(CT.rt); printf("%d\n",f[CT.rt][m]);
	}
}

F. Circular Xor Reversal

人类智慧构造题,想破脑袋也想不出来只能去看Tutorial

考虑定义一种操作\(f(l,r)\),可以把\([l,r]\)的前半部分异或上后半部分的翻转,具体的:

\[a_l=a_l\oplus a_r\\ a_{l+1}=a_{l+1}\oplus a_{r-1}\\ a_{l+2}=a_{l+2}\oplus a_{r-2}\\ \cdots \]

注意由于序列是循环的,因此\(l>r\)是合法的,只要在模意义下处理下标即可,同时我们记\(m=(j-i+n)\bmod n\)

不难发现当\(n\)为偶数时,执行\(f(0,n-1),f(\frac{n}{2},\frac{n}{2}-1),f(0,n-1)\)后即可实现翻转序列

而当\(n\)为奇数时,执行\(f(0,n-1),f(\frac{n+1}{2},\frac{n-3}{2}),f(0,n-1)\)即可

考虑处理\(f(l,r)\),为了方便表示我们用\(b_{0\sim m}\)表示\(a_{l\sim r}\)

首先考虑从\(m-1\sim0\)依次操作,得到\(b_0\oplus\cdots \oplus b_m,b_1\oplus\cdots\oplus b_m,\cdots,b_m\)

然后从\(1\sim m-1\)依次操作,得到\(b_0\oplus\cdots \oplus b_m,b_1,\cdots,b_m\)

然后我们对\(1\sim m-1\)重复上面的过程,得到\(b_0\oplus\cdots \oplus b_m,b_1\oplus\cdots\oplus b_{m-1},\cdots,b_{\lfloor\frac{m-1}{2}\rfloor}\oplus b_{\lfloor\frac{m+2}{2}\rfloor},b_{\lfloor\frac{m+1}{2}\rfloor},\cdots,b_m\)

然后从\(1\sim \lfloor\frac{m-2}{2}\rfloor\)即可得到\(b_0\oplus b_m,b_1\oplus b_{m-1},\cdots,b_{\lfloor\frac{m-1}{2}\rfloor}\oplus b_{\lfloor\frac{m+2}{2}\rfloor},b_{\lfloor\frac{m+1}{2}\rfloor},\cdots,b_m\),完成了上述构造

考虑它的操作次数,为\(m+m-1+m-2+\cdots+1+\frac{m}{2}=\frac{m^2+2m}{2}<\frac{250000}{3}\),因此符合题意

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=405;
int n,a[N],ans[250005],cnt;
inline void solve(int l,int r)
{
	RI i; if (l>r) r+=n; int m=r-l,st=l,sign=1; --r;
	while (l<=r)
	{
		if (sign==1)
		{
			for (i=r;i>=l;--i) ans[++cnt]=i%n; ++l;
		} else
		{
			for (i=l;i<=r;++i) ans[++cnt]=i%n; --r;
		}
		sign^=1;
	}
	for (i=st;i<st+m/2;++i) ans[++cnt]=i%n;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; scanf("%d",&n); solve(0,n-1); solve(n+1>>1,n-2>>1); solve(0,n-1);
	for (printf("%d\n",cnt),i=1;i<=cnt;++i) printf("%d ",ans[i]);
	return 0;
}

Postscript

下场找ztc和我一起打,感觉我们EZ这一届选择打ACM的好少(除了LTL和XZY两个和XYZ组成QZEZ代表队的巨佬)

现在就是写题很容易思路不清犯个SB错误,然后一下看不出来心态爆炸直接GG

连掉5场了真掉不起了呜呜呜

posted @ 2022-11-14 21:24  空気力学の詩  阅读(62)  评论(0编辑  收藏  举报