Codeforces Round 963 (Div. 2)

Preface

有懒狗上周日的比赛拖到这周日才写博客,我不说是谁

这场比赛的时候因为 C 数组没开两倍卡了 1h 最后写对拍才看出来,直接心态爆炸导致 D 没写完掉大分


A. Question Marks

签到

#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=405;
int t,n; char s[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%s",&n,s+1);
		int c[4]={0,0,0,0},ans=0;
		for (RI i=1;i<=4*n;++i)
		if (s[i]!='?') ++c[s[i]-'A'];
		for (RI i=0;i<4;++i) ans+=min(c[i],n);
		printf("%d\n",ans);
	}
	return 0;
}

B. Parity and Sum

注意到偶数与奇数的和依然为奇数,因此我们的策略总是把所有偶数变成奇数

拿出最大的奇数,从小到大和所有偶数比较,能操作就吸收这个偶数得到更大的奇数;否则直接找一个奇数和最大的偶数操作后得到一个最大的数,这个数一定可以和所有偶数进行操作

#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];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d",&n); vector <int> odd,even;
		for (RI i=1;i<=n;++i)
		{
			scanf("%d",&a[i]);
			if (a[i]%2==1) odd.push_back(a[i]); else even.push_back(a[i]);
		}
		if (odd.empty()||even.empty()) { puts("0"); continue; }
		sort(odd.begin(),odd.end());
		sort(even.begin(),even.end());
		int flag=0; LL cur=odd.back();
		for (auto x:even) if (cur<x) { flag=1; break; } else cur+=x;
		printf("%d\n",(int)even.size()+flag);
	}
	return 0;
}

C. Light Switches

不难发现灯的状态以 \(2k\) 为周期变化,因此很容易想到把所有 \(t_i\) 投射到 \([0,2k-1]\) 的数组上

此时我们需要找到一个长为 \(k\) 的区间 \([l,r]\) 使得其包含所有的数,注意这个区间是在模意义下的可以跨过末尾

最后答案为最小的 \(T\equiv r\pmod {2k}\),且所有的 \(t_i\le T\),简单枚举即可

#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,k,a[N],c[N*2];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d",&n,&k);
		for (RI i=0;i<2*k;++i) c[i]=-1;
		for (RI i=1;i<=n;++i)
		scanf("%d",&a[i]),c[a[i]%(2*k)]=max(c[a[i]%(2*k)],a[i]);
		vector <int> pos;
		for (RI i=0;i<2*k;++i)
		if (c[i]!=-1) pos.push_back(i);
		if (pos.size()==1) { printf("%d\n",c[pos[0]]); continue; }
		bool flag=0;
		for (RI i=0;i<pos.size()-1;++i)
		{
			int l=pos[i+1],r=2*k+pos[i];
			if (r-l<k)
			{
				int ans=0; for (auto x:pos)
				if (x>=l) ans=max(ans,c[x]+r-x); else ans=max(ans,c[x]+r-2*k-x);
				printf("%d\n",ans); flag=1; break;
			}
		}
		if (!flag)
		{
			int l=pos[0],r=pos.back();
			if (r-l<k)
			{
				int ans=0; for (auto x:pos)
				ans=max(ans,c[x]+r-x);
				printf("%d\n",ans); flag=1;
			}
		}
		if (!flag) puts("-1");
	}
	return 0;
}

D. Med-imize

中位数的处理套路就是二分答案 \(x\),然后将 \(\ge x\) 的数置为 \(1\)\(<x\) 的数置为 \(0\)(置为 \(-1\) 也行),最后就是要使得留下的数的和大于某个定值

假设最后留下了 \(R\) 个数,观察这些数需要满足什么限制,手玩后发现它们下标对 \(k\) 取模的值从左到右一定为 \(1,2,\dots,k\)

因此很容易想到 DP,令 \(f_{i}\) 表示处理了前 \(i\) 个数,且最后选的数下标模 \(k\) 的值和 \(i\bmod k\) 相同的最大权值和,转移显然

#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=500005;
int t,n,k,rem,a[N],f[N];
inline int calc(CI lim)
{
	int ret=0; for (RI i=1;i<=n;++i)
	{
		if ((i-1)%k+1>rem) { f[i]=0; continue; }
		if ((i-1)%k==0)
		{
			f[i]=(a[i]>=lim);
			if (i>=k) f[i]=max(f[i],f[i-k]);
		}else
		{
			f[i]=f[i-1]+(a[i]>=lim);
			if (i>=k) f[i]=max(f[i],f[i-k]);
		}
		ret=max(ret,f[i]);
	}
	return ret;
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d",&n,&k);
		for (RI i=1;i<=n;++i) scanf("%d",&a[i]);
		if (n<=k)
		{
			sort(a+1,a+n+1);
			printf("%d\n",a[(n+1)/2]);
			continue;
		}
		rem=n%k; if (rem==0) rem+=k;
		int l=1,r=1e9,mid,ret;
		while (l<=r) if (calc(mid=l+r>>1)>=rem/2+1) ret=mid,l=mid+1; else r=mid-1;
		printf("%d\n",ret);
	}
	return 0;
}

E. Xor-Grid Problem

观察到核心性质后就不难的一个题

不妨给矩形多扩展一行一列,其中最后一行的数字代表这列所有数字的异或和;最后列的数字代表这行所有数字的异或和

此时不难发现每个操作等价于交换某一行和最后一行,以及交换某一列和最后一列

但我们最后计算答案时不考虑多出的这一行一列,因此需要在选出某行某列删去后,将剩下的行列任意排布得到答案

不难发现行和列的贡献可以分开计算,以行为例,任意两行相邻排布的贡献就是它们对应列(去掉被删除的列后)上元素差的绝对值和

这形成了一个类似哈密顿路径的问题,可以简单状压 DP 解决,最后将删去某行某列的答案合并即可

认为 \(n,m\) 同阶,总复杂度 \(O(2^n\times n^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=16,INF=1e9;
int t,n,m,a[N][N],dr[N][N],dc[N][N],R[N][N],C[N][N],f[1<<N][N];
inline void solve(int d[N][N],CI n)
{
	for (RI i=0;i<(1<<n+1);++i)
	for (RI j=0;j<=n;++j) f[i][j]=INF;
	for (RI i=0;i<=n;++i) f[1<<i][i]=0;
	for (RI mask=0;mask<(1<<n+1);++mask)
	for (RI i=0;i<=n;++i) if ((mask>>i)&1)
	for (RI j=0;j<=n;++j) if (!((mask>>j)&1))
	f[mask|(1<<j)][j]=min(f[mask|(1<<j)][j],f[mask][i]+d[i][j]);
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d",&n,&m);
		for (RI i=0;i<=n;++i) a[i][m]=0;
		for (RI j=0;j<=m;++j) a[n][j]=0;
		for (RI i=0;i<n;++i) for (RI j=0;j<m;++j)
		scanf("%d",&a[i][j]),a[i][m]^=a[i][j],a[n][j]^=a[i][j];
		for (RI i=0;i<n;++i) a[n][m]^=a[i][m];
		for (RI rmv_c=0;rmv_c<=m;++rmv_c)
		{
			for (RI i=0;i<=n;++i) for (RI j=0;j<=n;++j)
			{
				if (i==j) continue; dr[i][j]=0;
				for (RI k=0;k<=m;++k)
				if (k!=rmv_c) dr[i][j]+=abs(a[i][k]-a[j][k]);
			}
			solve(dr,n);
			for (RI i=0;i<=n;++i)
			{
				R[i][rmv_c]=INF; int mask=((1<<n+1)-1)^(1<<i);
				for (RI j=0;j<=n;++j)
				if ((mask>>j)&1) R[i][rmv_c]=min(R[i][rmv_c],f[mask][j]);
			}
		}
		for (RI rmv_r=0;rmv_r<=n;++rmv_r)
		{
			for (RI i=0;i<=m;++i) for (RI j=0;j<=m;++j)
			{
				if (i==j) continue; dc[i][j]=0;
				for (RI k=0;k<=n;++k)
				if (k!=rmv_r) dc[i][j]+=abs(a[k][i]-a[k][j]);
			}
			solve(dc,m);
			for (RI i=0;i<=m;++i)
			{
				C[rmv_r][i]=INF; int mask=((1<<m+1)-1)^(1<<i);
				for (RI j=0;j<=m;++j)
				if ((mask>>j)&1) C[rmv_r][i]=min(C[rmv_r][i],f[mask][j]);
			}
		}
		int ans=INF; for (RI i=0;i<=n;++i) for (RI j=0;j<=m;++j)
		ans=min(ans,R[i][j]+C[i][j]); printf("%d\n",ans);
	}
	return 0;
}

F1. Dyn-scripted Robot (Easy Version)

感觉比 D,E 都简单,算是个经典套路题

对于这类反射类问题一个经典的处理就是不改变机器人的移动方向,而是将整个外框镜像一下,这样就不涉及修改操作序列了

手玩下会发现当机器人走到 \((x,y)\) 时,在原矩形中的位置相当于 \((x\bmod 2w,y\bmod 2h)\)

因此这题很容易求解,我们先求出进行一整个序列的操作后坐标的偏移量 \(\Delta x,\Delta y\),然后枚举之前进行了 \(i\in[0,k-1]\) 次完整的操作

要回到 \((0,0)\) 等价于在某个位置的偏移量为 \((-i\times \Delta x,-i\times \Delta y)\)(模意义下),用 map 统计下即可

#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=1e6+5;
int t,n,k,w,h; char s[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d%d%d%s",&n,&k,&w,&h,s+1);
		map <pi,int> rst; int x=0,y=0;
		for (RI i=1;i<=n;++i)
		{
			switch (s[i])
			{
				case 'U': ++y; break;
				case 'D': --y; break;
				case 'R': ++x; break;
				case 'L': --x; break;
			}
			x=(x%(2*w)+2*w)%(2*w);
			y=(y%(2*h)+2*h)%(2*h);
			++rst[{x,y}];
		}
		LL ans=0; for (RI i=0;i<k;++i)
		{
			int dx=(-1LL*i*x%(2*w)+2*w)%(2*w);
			int dy=(-1LL*i*y%(2*h)+2*h)%(2*h);
			ans+=rst[{dx,dy}];
		}
		printf("%lld\n",ans);
	}
	return 0;
}

Postscript

感觉最近不管什么比赛只要不顺心就容易红温犯病,明明已经算是老登了心态还是一如既往的烂

今晚还有 Div1+2,周末还要去北京打百度之星的决赛,希望别暴毙地太难看了吧

posted @ 2024-08-11 20:03  空気力学の詩  阅读(244)  评论(2)    收藏  举报