Codeforces Round #956 (Div. 2) and ByteRace 2024

Preface

连着好几天因为熬夜看LOL比赛导致白天精神萎靡,都没精力VP了

而且明天就要开始统一训练了,趁着最后一天补一下前两天因为看比赛没打的这场吧

这场只能说是战术正确,想了会E没啥思路就马上转头去把F写了,后面回头慢慢想E也想出来了,最后极限2h14min出了E


A. Array Divisibility

签到,直接令 \(a_i=i\) 就能得到一组合法解

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=105;
int t,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)
		printf("%d%c",i," \n"[i==n]);
	}
	return 0;
}

B. Corner Twist

不难发现操作可以在保证每行每列的和关于 \(3\) 取模下不变的前提下任意修改每个位置的值,因此比较每行每列的和即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=505;
int t,n,m; char a[N][N],b[N][N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; scanf("%d%d",&n,&m); bool flag=1;
		for (i=1;i<=n;++i) scanf("%s",a[i]+1);
		for (i=1;i<=n;++i) scanf("%s",b[i]+1);
		for (i=1;i<=n&&flag;++i)
		{
			int suma=0,sumb=0;
			for (j=1;j<=m;++j) suma+=a[i][j]-'0',sumb+=b[i][j]-'0';
			if (suma%3!=sumb%3) flag=0;
		}
		for (j=1;j<=m&&flag;++j)
		{
			int suma=0,sumb=0;
			for (i=1;i<=n;++i) suma+=a[i][j]-'0',sumb+=b[i][j]-'0';
			if (suma%3!=sumb%3) flag=0;
		}
		puts(flag?"YES":"NO");
	}
	return 0;
}

C. Have Your Cake and Eat It Too

首先 \(3!\) 枚举下从前往后的每一段分别是由哪个人拿走

通过 two pointers 枚举中间那段的极小的合法区间,然后检验前后缀是否合法即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,x,lim,pfx[3][N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i,j; for (scanf("%lld",&n),j=0;j<3;++j)
		for (i=1;i<=n;++i) scanf("%lld",&x),pfx[j][i]=pfx[j][i-1]+x;
		lim=(pfx[0][n]+2)/3;
		auto solve=[&](CI a,CI b,CI c)
		{
			auto sum=[&](CI id,CI l,CI r)
			{
				if (l>r) return 0LL;
				return pfx[id][r]-pfx[id][l-1];
			};
			for (RI i=1,j=1;i<=n;++i)
			{
				while (j<=i&&sum(b,j,i)>=lim) ++j;
				if (j>1) --j;
				if (sum(b,j,i)>=lim&&sum(a,1,j-1)>=lim&&sum(c,i+1,n)>=lim)
				{
					pi ans[3];
					ans[a]=pi(1,j-1); ans[b]=pi(j,i); ans[c]=pi(i+1,n);
					for (RI k=0;k<3;++k) printf("%d %d ",ans[k].fi,ans[k].se);
					putchar('\n'); return 1;
				}
			}
			return 0;
		};
		if (solve(0,1,2)) continue;
		if (solve(0,2,1)) continue;
		if (solve(1,0,2)) continue;
		if (solve(1,2,0)) continue;
		if (solve(2,0,1)) continue;
		if (solve(2,1,0)) continue;
		puts("-1");
	}
	return 0;
}

D. Swap Dilemma

首先判掉 \(a,b\) 对应的多重集不同的情况,然后看到交换我们就想到逆序对

由于每次交换两个位置序列的逆序对的奇偶性一定会发生改变,因此大力猜测一手有解的充要条件是 \(a,b\) 的逆序对数量奇偶性相同

必要性上面已经说明;充分性可以考虑只交换相邻两个元素,我们可以对一个序列进行重复操作来保持其不变,让另一个序列向其变化即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,a[N],b[N],ca[N],cb[N];
class Tree_Array
{
	private:
		int bit[N];
	public:
		#define lowbit(x) (x&-x)
		inline int get(RI x,int ret=0)
		{
			for (;x<=200000;x+=lowbit(x)) ret+=bit[x]; return ret;
		}
		inline void add(RI x,CI y)
		{
			for (;x;x-=lowbit(x)) bit[x]+=y;
		}
		#undef lowbit
}BIT;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d",&n); bool flag=1;
		for (i=1;i<=n;++i) scanf("%d",&a[i]),++ca[a[i]];
		for (i=1;i<=n;++i) scanf("%d",&b[i]),++cb[b[i]];
		for (i=1;i<=n;++i) if (ca[a[i]]!=cb[a[i]]) { flag=0; break; }
		for (i=1;i<=n;++i) ca[a[i]]=cb[b[i]]=0;
		if (!flag) { puts("NO"); continue; }
		LL suma=0; for (i=1;i<=n;++i)
		suma+=BIT.get(a[i]+1),BIT.add(a[i],1);
		for (i=1;i<=n;++i) BIT.add(a[i],-1);
		LL sumb=0; for (i=1;i<=n;++i)
		sumb+=BIT.get(b[i]+1),BIT.add(b[i],1);
		for (i=1;i<=n;++i) BIT.add(b[i],-1);
		puts(suma%2==sumb%2?"YES":"NO");
	}
	return 0;
}

E. I Love Balls

首先我们只要知道了每个人拿到的特殊球和普通球的个数就可以根据期望的线性性来计算答案了

同时不难发现先手拿到的普通球个数就是 \(\lceil \frac{n-k}{2} \rceil\),难点在于计算先手拿到多少个特殊球

有一个naive的DP想法,即设 \(f_{i,j}\) 表示在有 \(i\) 个特殊球,\(j\) 个普通球的局面下,先手拿到的特殊球数量的期望,转移很显然:

\[f_{i,j}=\frac{i}{i+j}\times (1+f_{i-1,j})+\frac{j}{i+j}\times (i-f_{i,j-1}) \]

直接这么做是 \(O(n^2)\) 的,考虑用更好的方法来计数

不妨从宏观的角度来思考,我们把 \(n\) 个球按照拿走的顺序构成一个排列,则 \(n-k\) 个特殊球把序列分成了 \(n-k+1\) 个间隔

每个特殊球在每个间隔中的概率是相同的,而先手会拿走其中 \(\lceil \frac{n-k+1}{2} \rceil\) 个间隔中的特殊球,因此先手拿走的球数量的期望为:

\[k\times \frac{\lceil \frac{n-k+1}{2} \rceil}{n-k+1} \]

剩下的就很好处理了,总复杂度 \(O(n)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int mod=1e9+7;
int t,n,k,x;
inline int sum(CI x,CI y)
{
	return x+y>=mod?x+y-mod:x+y;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%d",&n,&k); int s1=0,s2=0;
		for (i=1;i<=k;++i) scanf("%d",&x),s1=sum(s1,x);
		for (i=1;i<=n-k;++i) scanf("%d",&x),s2=sum(s2,x);
		int ans1=sum(1LL*k*((n-k+2)/2)%mod*quick_pow(n-k+1)%mod*s1%mod*quick_pow(k)%mod,1LL*((n-k+1)/2)*s2%mod*quick_pow(n-k)%mod);
		printf("%d %d\n",ans1,(sum(s1,s2)-ans1+mod)%mod);
	}
	return 0;
}

F. array-value

求第 \(k\) 大值一眼想到二分答案 \(x\),转化为求贡献 \(\le x\) 的区间数量

考虑对于某个右端点 \(i\),令 \(p_i\) 表示最大的 \(j<i\),满足 \(a_j\oplus a_i\le x\)

这样我们只要把 \(\{p\}\) 数组做前缀最大值后,再把所有位置的值加起来就是合法的区间数量了

而要求 \(p_i\) 也很简单,\(a_j\oplus a_i\le x\) 是非常经典的用 0/1Trie 处理的套路了:即考虑在 \(x\)\(1\) 的那些二进制位上保持前缀不变,然后让 \(a_j\oplus a_i\) 这一位取 \(0\),后面的就可以随便取了

0/1Trie 中每个节点维护子树内最靠右的坐标值即可,总复杂度 \(O(n\log^2 a_i)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,a[N],rt; LL k;
class Trie
{
	private:
		int ch[N*30][2],mxp[N*30],idx;
	public:
		inline void clear(void)
		{
			for (RI i=1;i<=idx;++i) ch[i][0]=ch[i][1]=mxp[i]=0; rt=idx=0;
		}
		inline void insert(int& now,CI x,CI pos,CI d=29)
		{
			if (!now) now=++idx;
			mxp[now]=max(mxp[now],pos);
			if (d==-1) return;
			insert(ch[now][(x>>d)&1],x,pos,d-1);
		}
		inline int query(CI now,CI x,CI lim,CI d=29)
		{
			if (!now||d==-1) return 0;
			if ((lim>>d)&1) return max(mxp[ch[now][(x>>d)&1]],query(ch[now][((x>>d)&1)^1],x,lim,d-1));
			else return query(ch[now][(x>>d)&1],x,lim,d-1);
		}
}T;
inline LL calc(CI lim)
{
	LL sum=0; int pos=0; T.clear();
	for (RI i=1;i<=n;++i)
	{
		pos=max(pos,T.query(rt,a[i],lim+1));
		sum+=pos; T.insert(rt,a[i],i);
	}
	return sum;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%lld",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]);
		int l=0,r=1<<30,mid,ret; while (l<=r)
		if (calc(mid=l+r>>1)>=k) ret=mid,r=mid-1; else l=mid+1;
		printf("%d\n",ret);
	}
	return 0;
}

Postscript

G题看了眼感觉挺复杂的直接弃疗了,感觉这场如果赛时打的话是个上大分的机会啊

posted @ 2024-07-09 18:26  空気力学の詩  阅读(407)  评论(0编辑  收藏  举报