Codeforces Round 899 (Div. 2)

Preface

好久没现场打CF了(玩CC玩的.jpg),但这场久违的打的还不错,把Kusanagi_Misuzu这个小号也打上橙了

虽然开场的时候状态不佳写的巨慢,但后面还是靠着ztc带我做出E1成功题数反超上大分

接下来要考虑启动第三个小号了,只敢打Div2的FW是这样的


A. Increasing Sequence

比赛时候降智了竟然想去写二分答案,后面写了一半发现我脑子可能有问题

直接从前往后依次检验贪心放是否合法即可

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=105;
int t,n,a[N],b[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]);
		for (i=1;i<=n;++i)
		{
			b[i]=b[i-1]+1;
			if (b[i]==a[i]) ++b[i];
		}
		printf("%d\n",b[n]);
	}
	return 0;
}

B. Sets and Union

刚开始拿到这个题还愣了一会,后面才反应过来是个傻逼题

考虑最后的\(S\)既然不能等于全集,那么至少存在一个和全集不同的元素

我们不妨大力枚举这个不同的元素是什么,那么贪心地把所有不包含这个元素的集合的并求出来更新答案即可

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=55;
int t,n,k[N],x,vis[N][N],ext[N]; vector <int> num[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; memset(vis,0,sizeof(vis)); memset(ext,0,sizeof(ext));
		for (scanf("%d",&n),i=1;i<=n;++i)
		for (scanf("%d",&k[i]),num[i].clear(),j=1;j<=k[i];++j)
		scanf("%d",&x),num[i].push_back(x),vis[x][i]=ext[x]=1;
		int ans=0; for (i=1;i<=50;++i) if (ext[i])
		{
			unordered_set <int> s;
			for (j=1;j<=n;++j) if (!vis[i][j])
			for (auto x:num[j]) s.insert(x);
			ans=max(ans,(int)s.size());
		}
		printf("%d\n",ans);
	}
	return 0;
}

C. Card Game

本来想了个比较复杂的做法,后面ztc和我说了才发现是个傻逼题

考虑枚举我们要拿的牌中第一张的位置,显然它后面的所有权值为正的牌我们都可以拿,顺带更新答案即可

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,a[N],suf[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]);
		for (suf[n+1]=0,i=n;i>=1;--i) suf[i]=suf[i+1]+(a[i]>0?a[i]:0LL);
		int ans=0; for (i=1;i<=n;++i) ans=max(ans,(i&1?a[i]:0LL)+suf[i+1]);
		printf("%lld\n",ans);
	}
	return 0;
}

D. Tree XOR

经典换根DP

首先考虑有根树怎么做,很容易发现由于我们对根节点进行任何操作都是不优的,因此第一步肯定是把所有根节点的子节点异或上它们两点点权的异或值,以把这些子节点的权值变成和根节点一致

然后往下推,这些点的子节点也要异或上它们与父亲的权值的异或和,剩下的点亦然

因此写成式子,总代价就是\(\sum_{x\ne root} (a_x\oplus a_{fa_x})\times sz_x\),可以很容易用一个DP来维护每个点子树内的答案

然后再考虑换根操作,稍微画一下图把每次变化的贡献搞清楚就很简单了

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,a[N],x,y,sz[N]; LL f[N],g[N]; vector <int> v[N];
inline void DFS1(CI now=1,CI fa=0)
{
	sz[now]=1; f[now]=0;
	for (auto to:v[now]) if (to!=fa) DFS1(to,now),sz[now]+=sz[to],f[now]+=f[to];
	if (fa) f[now]+=1LL*(a[now]^a[fa])*sz[now];
}
inline void DFS2(CI now=1,CI fa=0)
{
	for (auto to:v[now]) if (to!=fa)
	g[to]=g[now]-1LL*(a[now]^a[to])*sz[to]+1LL*(a[now]^a[to])*(n-sz[to]),DFS2(to,now);
}
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]),v[i].clear();
		for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
		for (DFS1(),g[1]=f[1],DFS2(),i=1;i<=n;++i) printf("%lld%c",g[i]," \n"[i==n]);
	}
	return 0;
}

E1. Two Permutations (Easy Version)

好题,虽然有很多直觉猜想的成分,但只要能过就行

首先看到题目就感觉是先找到针对单个串的还原方案,然后合并两个串的答案

刚开始想的是由于我们可以通过重复操作\(1,|S|\)来使得一个串不发生改变,因此当且仅当两个串的答案长度的差值为偶数时才有解

但我们伟大的先驱者ztc写完交了一发发现挂了,因此后面随即发现另一个重要的corner case

当某个串长度为奇数时,对其进行串长次\(1\)操作即可在不改变它本身的情况下使得它的答案长度的奇偶性发生改变,从而把原来的无解情况变成有解情况

讨论完合并后我们考虑如何对于一个串求解,这里的思路比较自然,我们考虑依次把每个数接到一段已经恢复顺序的前缀后面

具体地,不妨设此时的序列形如\(x,\cdots,x,1,2,\cdots,i,x,\cdots,x,i+1,x,\cdots,x\),我们想要把\(i+1\)接到\(i\)的后面而保证前面已经得到的\(1\sim i\)也依然有序

\(pos(t)\)表示数\(t\)所在的位置,稍微手玩下会发现只要依次操作pivot(pos(i)+1),pivot(pos(i+1))即可

不难发现这样对于单个串的操作上限为\(2n\)次,加上改变奇偶性需要的\(n\)次,总操作次数为\(3n=7500\le 10000\),满足题意

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2505;
int n,m,x[N<<2],y[N<<2]; vector <int> a,b;
inline VI pivot(VI a,CI n,CI pos)
{
	VI b; RI i; for (i=pos+1;i<n;++i) b.push_back(a[i]);
	b.push_back(a[pos]);
	for (i=0;i<pos;++i) b.push_back(a[i]);
	return b;
}
inline VI solve(VI a,CI n)
{
	RI i,j; VI res; for (i=0;i<n-1;++i)
	{
		int pos; for (j=0;j<n;++j) if (a[j]==i) { pos=j; break; }
		if (pos!=n-1&&a[pos+1]==i+1) continue;
		if (pos!=n-1) a=pivot(a,n,pos+1),res.push_back(pos+1);
		for (j=0;j<n;++j) if (a[j]==i+1) { pos=j; break; }
		a=pivot(a,n,pos); res.push_back(pos);
	}
	return res;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; scanf("%d%d",&n,&m); a.resize(n); b.resize(m);
	for (i=0;i<n;++i) scanf("%d",&a[i]),--a[i];
	for (i=0;i<m;++i) scanf("%d",&b[i]),--b[i];
	VI A=solve(a,n),B=solve(b,m);
	if (A.size()%2!=B.size()%2)
	{
		if (n%2==0&&m%2==0) return puts("-1"),0;
		if (n%2==1)
		{
			VI tmp; for (i=0;i<n;++i) tmp.push_back(0);
			for (auto x:A) tmp.push_back(x); A=tmp;
		} else if (m%2==1)
		{
			VI tmp; for (i=0;i<m;++i) tmp.push_back(0);
			for (auto x:B) tmp.push_back(x); B=tmp;
		}
	}
	if (A.size()<=B.size())
	{
		for (i=0;i<A.size();++i) x[i+1]=A[i];
		for (i=1;i<=B.size()-A.size();i+=2) x[A.size()+i]=0,x[A.size()+i+1]=n-1;
		for (i=0;i<B.size();++i) y[i+1]=B[i];
	} else
	{
		for (i=0;i<B.size();++i) y[i+1]=B[i];
		for (i=1;i<=A.size()-B.size();i+=2) y[B.size()+i]=0,y[B.size()+i+1]=m-1;
		for (i=0;i<A.size();++i) x[i+1]=A[i];
	}
	int lim=max(A.size(),B.size()); printf("%d\n",lim);
	for (i=1;i<=lim;++i) printf("%d %d\n",x[i]+1,y[i]+1);
	return 0;
}

E2. Two Permutations (Hard Version)

E2其实和E1是完全不同的两个题了,看了Tutorial发现做法很巧妙完全想不到,但没太懂怎么实现就先坑了


Postscript

这场题还挺符合我胃口的属于是,久违的上一波大分了

posted @ 2023-09-27 10:55  空気力学の詩  阅读(101)  评论(0编辑  收藏  举报