Educational Codeforces Round 161 (Rated for Div. 2)

Preface

刚好小小号2000+的时候遇到了这场理论上最适合我的手速场,然后被创飞了

过完ABC后看E过的人多,然后一眼构造了个二进制分解的做法,结果脑抽了以为\(64+63+……+1<2\times 64<200\),直接大力Rush假做法还不自知

后面冷静下来20min把DE都写了,本来1h5题下班的结果差点在比赛结束前没写完

再加上当天下午的VP也是打的一坨,鉴定为玩海猫玩的


A. Tricky Template

签到

#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=25;
int t,n; char a[N],b[N],c[N];
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%s%s%s",&n,a+1,b+1,c+1);
		bool flag=0; for (i=1;i<=n&&!flag;++i)
		{
			if (a[i]==b[i]&&a[i]!=c[i]) flag=1;
			if (a[i]!=b[i]&&a[i]!=c[i]&&b[i]!=c[i]) flag=1;
		}
		puts(flag?"YES":"NO");
	}
	return 0;
}

B. Forming Triangles

\(a_i,a_j,a_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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005;
int t,n,a[N],c[N];
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),++c[a[i]];
		LL ans=0,pre=0; for (i=0;i<=n;++i)
		ans+=1LL*c[i]*(c[i]-1)/2*pre+1LL*c[i]*(c[i]-1)*(c[i]-2)/6,pre+=c[i],c[i]=0;
		printf("%lld\n",ans);
	}
	return 0;
}

C. Closest Cities

规定如果\(s_i=L\)表示\(i\)的最近城市在左边,\(s_i=R\)同理

不妨假设\(x<y\),由于跳转到最近城市一定比走过去优,因此在\(x\to y\)的过程中遇到\(s_i=R\)的位置一定选择跳转,否则只能直接走过去

拿个前缀和维护一下\(R\)的个数以及\(L\)对应的城市向右走的代价即可,\(x>y\)的情况同理

#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=100005;
int t,n,m,x,y,a[N],L[N],R[N],LV[N],RV[N]; char s[N];
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
		for (s[1]='R',s[n]='L',i=2;i<n;++i) if (a[i]-a[i-1]<a[i+1]-a[i]) s[i]='L'; else s[i]='R';
		for (i=1;i<=n;++i) L[i]=L[i-1]+(s[i]=='L'),R[i]=R[i-1]+(s[i]=='R');
		for (i=1;i<n;++i) LV[i]=LV[i-1]+(s[i]=='L'?a[i+1]-a[i]:0);
		for (i=2;i<=n;++i) RV[i]=RV[i-1]+(s[i]=='R'?a[i]-a[i-1]:0);
		auto sum=[&](int *pfx,CI l,CI r)
		{
			return pfx[r]-pfx[l-1];
		};
		for (scanf("%d",&m),i=1;i<=m;++i)
		if (scanf("%d%d",&x,&y),x<y) printf("%d\n",sum(R,x,y-1)+sum(LV,x,y-1));
		else printf("%d\n",sum(L,y+1,x)+sum(RV,y+1,x));
	}
	return 0;
}

D. Berserk Monsters

经典大暴力题,刚开始还想了一堆奇奇怪怪的做法,后方发现就是个Brute Force

一个trivial的思路就是直接暴力模拟每一轮,但如果每轮死的怪数量很少的话就会被卡到\(O(n^2)\)

稍作思考会发现其实上一轮死掉的怪只会对与它相邻的位置的没死的怪产生影响,我们只要更新这些怪的状态即可

用链表和set来实现,复杂度\(O(n\log 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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005;
int t,n,a[N],d[N],L[N],R[N],ans[N],vis[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) scanf("%d",&d[i]),ans[i]=vis[i]=0;
		for (R[0]=1,L[n+1]=n,i=1;i<=n;++i) L[i]=i-1,R[i]=i+1;
		vector <int> death; for (a[0]=a[n+1]=0,i=1;i<=n;++i)
		if (a[i-1]+a[i+1]>d[i]) death.push_back(i);
		int turn=0; while (death.size()>0)
		{
			ans[++turn]=death.size(); set <int> mdy;
			for (auto x:death)
			{
				if (L[x]>=1) mdy.insert(L[x]);
				if (R[x]<=n) mdy.insert(R[x]);
			}
			for (auto x:death)
			{
				vis[x]=1; int pre=L[x],nxt=R[x]; R[pre]=nxt; L[nxt]=pre;
			}
			death.clear(); for (auto x:mdy)
			{
				if (vis[x]) continue;
				if (a[L[x]]+a[R[x]]>d[x]) death.push_back(x);
			}
		}
		for (i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
	}
	return 0;
}

E. Increasing Subsequences

想到关键处就很简单的构造题

刚开始发现的是如果有一个长度为\(x\)的单调递增段,其贡献为\(2^x\),于是想到二进制分解构造

但由于开头讲的智障错误以及一些奇怪的细节(比如怎么处理空集的方案),写了好久的假做法

后面冷静下来发现一个极其好写的做法,不妨考虑从大往小地向序列中加数

假设当前序列的答案为\(k\),如果加一个比之前所有数都小的数在最左边,那么新生成的序列的答案就为\(2k\);同理加在右边的话新生成的序列的答案就为\(k+1\)

利用这个就可以很容易地从初始状态\(k=2\)推出目标数了,同时不难发现操作次数是\(2\times \log X\)级别的

#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;
int t,X;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		scanf("%lld",&X); int high;
		for (RI i=61;i>=0;--i) if ((X>>i)&1LL) { high=i; break; }
		deque <int> ans; ans.push_back(0); int lst=0;
		for (RI i=high-1;i>=1;--i)
		if ((X>>i)&1LL) ans.push_back(--lst),ans.push_front(--lst); else ans.push_front(--lst);
		if (X&1LL) ans.push_back(--lst);;
		printf("%lld\n",ans.size());
		for (auto x:ans) printf("%lld ",x); putchar('\n');
	}
	return 0;
}

F. Replace on Segment

挺可做的DP题,如果比赛时候有1h可能还真能写出来

首先一眼区间DP,设状态\(f_{l,r,k}\)表示将区间\([l,r]\)中所有数变成\(k\)的最少操作数

转移大体上分两类,一类是经典的枚举分界点,将两个子区间都分别变成\(k\)

令一类就是先把\([l,r]\)中所有等于\(k\)的数变成不是\(k\),最后再对整个区间操作将其全部变成\(k\)

而处理这个过程也可以用DP,不妨设\(g_i\)表示将\([l,r]\)中前\(i\)\(k\)都变成不是\(k\)时所需的最少操作数

不难发现这个转移是一段一段的,可以枚举前一个分界点来转移

现在的问题就是怎么快速计算转移的代价,不难发现我们只要找操作数最小的且不等于\(k\)的某个数来转移即可,因此可以对每个区间维护一个全部变成某个数的最小值和次小值

总复杂度\(O(n^3\times x)\)

#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,INF=1e9;
int t,n,m,a[N],f[N][N][N],mn[N][N],smn[N][N]; vector <int> pos[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j,k,l; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
		if (m==1) { puts("0"); continue; }
		for (i=1;i<=n;++i) for (j=i;j<=n;++j) for (mn[i][j]=smn[i][j]=k=0;k<=m;++k) f[i][j][k]=INF;
		for (i=1;i<=n;++i) for (mn[i][i]=a[i],smn[i][i]=(a[i]==1?2:1),j=1;j<=m;++j) f[i][i][j]=(a[i]==j?0:1);
		for (RI len=2;len<=n;++len) for (i=1;i+len-1<=n;++i)
		{
			j=i+len-1; for (k=1;k<=m;++k) for (l=i;l<j;++l) f[i][j][k]=min(f[i][j][k],f[i][l][k]+f[l+1][j][k]);
			for (k=1;k<=m;++k) pos[k].clear();
			for (l=i;l<=j;++l) pos[a[l]].push_back(l);
			for (k=1;k<=m;++k)
			{
				if (pos[k].empty()) { f[i][j][k]=min(f[i][j][k],1); continue; }
				static int g[N]; g[0]=1; for (l=1;l<=pos[k].size();++l) g[l]=INF;
				for (RI p=0;p<pos[k].size();++p) for (RI q=0;q<=p;++q)
				{
					int L=pos[k][q],R=pos[k][p];
					if (mn[L][R]==k) g[p+1]=min(g[p+1],g[q]+f[L][R][smn[L][R]]);
					else g[p+1]=min(g[p+1],g[q]+f[L][R][mn[L][R]]);
				}
				f[i][j][k]=min(f[i][j][k],g[pos[k].size()]);
			}
			for (k=1;k<=m;++k)
			if (f[i][j][k]<=f[i][j][mn[i][j]]) smn[i][j]=mn[i][j],mn[i][j]=k;
			else if (f[i][j][k]<=f[i][j][smn[i][j]]) smn[i][j]=k;
		}
		int ans=INF; for (k=1;k<=m;++k) ans=min(ans,f[1][n][k]);
		printf("%d\n",ans);
	}
	return 0;
}

Postscript

byd怎么感觉越来越菜了说是

posted @ 2024-01-19 15:19  空気力学の詩  阅读(120)  评论(0)    收藏  举报