AtCoder Regular Contest 164

发布时间 2023-08-29 21:57:14作者: 空気力学の詩

Preface

补一下好久之前的ARC,ABC的话如果没事会考虑从后往前补一下


A - Ternary Decomposition

首先判掉当\(k>n\)时一定无解,否则可以贪心地对\(n\)进行三进制分解,得到最少可以拆成\(k'\)个数

不难发现我们总可以把其中较大的数拆成三个小的,以此来消耗掉两次操作

因此若\(k'<k\)的话就看\(k-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<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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n,k,pw[40];
inline void init(CI n)
{
	pw[0]=1; for (RI i=1;i<=n;++i) pw[i]=3LL*pw[i-1];
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t),init(37);t;--t)
	{
		RI i; scanf("%lld%lld",&n,&k);
		if (k>n) { puts("No"); continue; }
		int cur=0; for (i=37;i>=0;--i) cur+=n/pw[i],n%=pw[i];
		if (cur>k) { puts("No"); continue; }
		puts((k-cur)&1?"No":"Yes");
	}
	return 0;
}

B - Switching Travel

手玩可以发现要走回去就一定要有一条连接同色点的边,同时这条边的两个端点能同过连接异色点的边连通

那么先用并查集维护下所有异色边,然后枚举同色边查询即可

#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<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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int n,m,c[N],x[N],y[N],fa[N];
inline int getfa(CI x)
{
	return x!=fa[x]?fa[x]=getfa(fa[x]):x;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x[i],&y[i]);
	for (i=1;i<=n;++i) scanf("%d",&c[i]),fa[i]=i;
	for (i=1;i<=m;++i) if (c[x[i]]!=c[y[i]]) fa[getfa(x[i])]=getfa(y[i]);
	for (i=1;i<=m;++i) if (c[x[i]]==c[y[i]]&&getfa(x[i])==getfa(y[i])) return puts("Yes"),0;
	return puts("No"),0;
}

C - Reversible Card Game

好简单的C题,感觉比前面两题都简单

不难发现两人的最优策略都是选取当前局面下翻转后的变化量最大的牌,因此拿一个堆维护下即可

#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<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int n,a[N],b[N]; priority_queue <tri> hp; LL ans;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d",&n),i=1;i<=n;++i)
	scanf("%d%d",&a[i],&b[i]),hp.push({a[i]-b[i],i,1});
	while (!hp.empty())
	{
		auto [dlt,id,tp]=hp.top(); hp.pop(); hp.push({-dlt,id,-tp});
		auto [Dlt,Id,Tp]=hp.top(); hp.pop(); ans+=Tp>0?a[Id]:b[Id];
	}
	return printf("%lld",ans),0;
}

D - 1D Coulomb

刚开始脑抽了以为就等价为合法括号序列,然后发现还是有点区别的,但后面发现稍微修改下也能过

首先手玩下第一个样例感觉就是个就近匹配,因此把+看成\(1\)-看成\(-1\),问题就变成合法括号序列匹配中每对括号间的距离和

这东西有个套路的维护方式,我们总是在加入左括号的时候累计贡献,同时当遇到右括号时就清除掉一个左括号的贡献

不妨设\(f_{i,j}\)表示以及处理了前\(i\)个位置,填的东西的前缀和为\(j\)时的方案数,\(g_{i,j}\)表示同状态下的距离和

\(f_{i,j}\)的转移很简单,而\(g_{i,j}\)只需要再加上\(f_{i,j}\times j\)即可,复杂度为\(O(n^2)\)

好家伙写完发现还可以匹配成-+这样,但无伤大雅直接允许第二维出现负数即可

#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<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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=6005,mod=998244353;
int n,f[N][N<<1],g[N][N<<1]; char s[N];
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%s",&n,s+1),n<<=1,i=1,f[0][N]=1;i<=n;++i)
	{
		if (s[i]!='-')
		{
			for (j=-i;j<=i;++j) inc(f[i][j+N],f[i-1][j-1+N]),inc(g[i][j+N],g[i-1][j-1+N]);
		}
		if (s[i]!='+')
		{
			for (j=-i;j<=i;++j) inc(f[i][j+N],f[i-1][j+1+N]),inc(g[i][j+N],g[i-1][j+1+N]);
		}
		for (j=-i;j<=i;++j) inc(g[i][j+N],1LL*f[i][j+N]*abs(j)%mod);
	}
	return printf("%d",g[n][N]),0;
}

E - Segment-Tree Optimization

好劲的题,首先考虑用更本质的形式表达一棵线段树,不妨用\([l,r]\)的划分点\(mid\)作为值把整棵树缩成一个序列,而相邻的\(mid\)值就组成了树中的所有区间

考虑询问的区间\([l_i,r_i]\)可以被拆成两个断点\(l_i-1,r_i\),要解决\(d\)最小只需要贪心地让这些断点都在缩成的序列中

\(1\sim d\)层共有\(\sum_{i=1}^d 2^{d-1}=2^d-1\)个断点,因此找到最小的\(d\)满足\(2^d-1\ge k\)即可

接下来考虑第二问,不难发现上面那种划分形式所有奇数下标的断点都在树的第\(d\)层,因此只有这些地方会产生贡献

设第\(d\)层的某个断点在\(l_i-1,r_i\)中共出现了\(k\)次,则它最后产生的贡献即为\(2k\),因为它左右两侧的区间都会被访问恰好\(k\)

那么就很好DP了,设\(f_{i,j}\)表示考虑了前\(i\)个断点,有\(j\)个被考虑后贡献之和,转移很显然

总复杂度\(O(n^2)\)

#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<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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=4005,INF=1e9;
int n,q,d,l,r,f[N<<1][N],c[N],a[N],cnt;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j; for (scanf("%d%d",&n,&q),i=1;i<=q;++i)
	scanf("%d%d",&l,&r),++c[l-1],++c[r];
	for (i=1;i<n;++i) if (c[i]) a[++cnt]=c[i];
	for (d=0;;++d) if ((1<<d)-1>=cnt) break;
	if (d==0) return printf("0 %d",q),0;
	for (i=0;i<(1<<d);++i) for (j=0;j<=cnt;++j) f[i][j]=INF;
	for (f[0][0]=0,i=1;i<(1<<d);++i) for (j=0;j<=cnt;++j)
	if (f[i][j]=f[i-1][j],j) f[i][j]=min(f[i][j],f[i-1][j-1]+(i&1?2*a[j]:0));
	return printf("%d %d",d,f[(1<<d)-1][cnt]),0;
}

Postscript

开学了冲冲冲