首页 > 其他分享 >Codeforces Round 876 (Div. 2)

Codeforces Round 876 (Div. 2)

时间:2023-06-04 18:03:14浏览次数:47  
标签:const 876 scanf Codeforces int Div include RI define

Preface

DP腐乳闪总出列!

(本来以为大掉分的一把,但这个号因为挺新的所以竟然还能上挺多分的,压线完成了5场上紫)

早知道去做E题了,感觉CF真得要看题目相性,有些题目就是一眼感觉不适合自己的说


A. The Good Array

一个要动点脑子的签到题,因为\(a_1,a_n\)必须等于\(1\),然后中间的\(n-1\)个元素不能出现连续的\(k\)个\(0\)

小推一下式子就是\(1+\lceil\frac{n-1}{k}\rceil\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,k;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	scanf("%d%d",&n,&k),printf("%d\n",1+(n-1+k-1)/k);
	return 0;
}

B. Lamps

这场上来给我的一个感觉就是题目好难读,难受捏

其实看清题目就很简单,因为一个灯泡爆了就不会被算到点亮的里面去

因此不难发现对于\(a_i=1\)的所有灯泡最多只能选其中一个点亮,而\(a_i=2\)的就只能选两个,依此类推

vector存下所有的灯泡然后排序选大的那些即可

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,k,x,y; vector <int> v[N]; long long ans;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) v[i].clear();
		for (i=1;i<=n;++i) scanf("%d%d",&x,&y),v[x].push_back(y);
		for (ans=0,i=1;i<=n;++i)
		{
			sort(v[i].begin(),v[i].end()); int sz=v[i].size();
			for (j=1;j<=min(i,sz);++j) ans+=v[i][sz-j];
		}
		printf("%lld\n",ans);
	}
	return 0;
}

C. Insert Zero and Invert Prefix

刚开始想歪了走到等价转化那条路上去了,然后浪费好多时间,后面发现直接倒着构造即可

从无解的情况出发,不难发现当\(a_n=1\)是一定无解的,否则一定有解,接着考虑如何构造

考虑从后往前把序列中的一段\(1\)开头,后面跟着一段\(0\)的序列进行划分

比如对于样例1 0 0 1 1 0,可以划分成一段1 1 0和一段1 0 0

而这样一段序列的构造是很显然的,设这一段的长度为\(k\),且其中\(1\)的个数为\(c\)

那我们只要进行\(k-1\)次\(0\)操作,然后用一次\(c\)操作把前面的\(c\)个\(0\)反转成\(1\)即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,a[N],cnt,ans[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j,k,cnt; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
		if (a[n]) { puts("NO"); continue; }
		for (i=n,cnt=0;i>=1;i=k)
		{
			for (j=i;j>=1&&a[i]==a[j];--j);
			for (k=j;k>=1&&a[j]==a[k];--k);
			for (RI t=1;t<i-k;++t) ans[++cnt]=0;
			ans[++cnt]=j-k;
		}
		for (puts("YES"),i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
	}
	return 0;
}

D. Ball Sorting

唉开始的基础转化就错了,导致后面一直深陷里面走不出来,本来挺简单的一个DP的说

比赛的时候思路大概是就是如果不考虑\(0\)球的个数限制,那么找出序列的LIS长度\(x\),则答案就是\(n-x\)

然后就在想如果有\(k\)个\(0\)球的限制,就会划分成\(k+1\)个段,然后贡献就是每段里面的LIS还要保证能首尾相接起来,但怎么搞都对不上样例

好家伙后面仔细一想,其实不能动的根本不是LIS,还要满足连续性,即一个极长的单调上升区间

其实也很好理解,考虑如果现在有一个\(0\)放在序列的开头,那么它至少要操作\(t\)次,满足序列剩下的\(n-t\)个数是单调升的,这样把前\(t\)个数插入进去才是对的

同理如果\(0\)在末尾的话就是找极长的单调上升前缀,而如果前后都有\(0\)的段就是找一段极长的单调上升区间

因此现在问题就转化为,把序列分为\(k+1\)段,要求一种方案使得每段内的极长单调上升区间总长度和最长,并且相邻的区间的收尾也满足单调升的条件

此时我们就很容易设计DP方程了,令\(f_{i,j}\)表示处理了前\(i\)个数,放了\(j\)个\(0\)球时最少要移动的球的数目

转移的话若\(a_i>a_{i-1}\)就直接从\(i-1\)处转移,否则枚举上一段内和它接在一起的位置\(k\)转移即可

具体实现看代码,想通了就非常简单的说,总复杂度\(O(n^3)\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=505,INF=1e9;
int t,n,a[N],f[N][N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j,k,t; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
		for (a[n+1]=INF,i=1;i<=n+1;++i) for (j=0;j<=n;++j) f[i][j]=INF;
		for (i=1;i<=n+1;++i) for (j=0;j<=n;++j)
		{
			if (a[i-1]<a[i]) f[i][j]=f[i-1][j];
			if (j) for (k=0;k<i;++k) if (a[k]<a[i]) f[i][j]=min(f[i][j],f[k][j-1]+i-k-1);
		}
		for (i=1;i<=n;++i) printf("%d%c",f[n+1][i]," \n"[i==n]);
	}
	return 0;
}

E. Decreasing Game

趣题,看似是博弈其实和博弈没太大关系,是个思博题

首先考虑如果我们存在一种划分方案,可以把\(n\)个数划分为两个集合\(A,B\),满足两个集合内元素的和相同,则此时一定后手必胜

具体的策略也很简单,若先手选择操作集合\(A\)中的数,我们就任意在集合\(B\)中取一个数即可,反之如果先手取\(B\)集合我们就取\(A\)集合

不难发现此时两个集合减去的数相同,因此一轮过后依然满足之前的性质,那么在若干轮后一定会变成两个集合中的数都是\(0\)的情况,因此是种必胜策略

那么有意思的地方就是不能划分的情况怎么处理,这里就考验人类智慧了,答案选择先手然后随便操作都能获胜(你没有看错就是随便做)

证明的话其实也很简单,假设在一轮随便操作后,使得当前局面变为了可划分成两个集合的局面,那么说明我们的策略有问题,因为此时对手作为后手可以用上面的策略击败我们

但考虑真的会出现这种局面吗,不妨设原来不存在这样一种划分方案,在一轮操作选出了数\(x,y\)(不妨设\(x\le y\))后,剩下的局面存在了可划分方案

当\(x=y\)的情况显然是矛盾的,不用考虑,因此现在只考虑\(x<y\)的情形

此时操作完会剩下一个数\(y-x\),不妨设\(y-x\)在后面的划分中被归到了集合\(A\)

设后面的划分中集合\(A\)的元素和是\(S\),那么\(A\)中其它元素的和就是\(S-(y-x)\),而\(B\)的元素和就是\(S\)

然后我们会发现如果剩下的局面有解,那么我们如果撤销对\(x,y\)的操作,把\(y\)加入集合\(A\),把\(x\)加入集合\(B\)

此时集合\(A\)的元素和就是\(S-(y-x)+y=S+x\),\(B\)的元素和就是\(S+x\),这说明原来就存在着一种划分方案,与我们的前提矛盾,因此结论成立

然后这题就做完了,确实是很有意思的说

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=305,INF=1e9;
int t,n,a[N],sum,f[N][N*N],g[N][N*N],tp[N];
int main()
{
	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),sum+=a[i];
	for (f[0][0]=1,i=0;i<n;++i) for (j=0;j<=sum;++j)
	if (f[i][j]) f[i+1][j]=f[i+1][j+a[i+1]]=g[i+1][j+a[i+1]]=1;
	if (sum%2||!f[n][sum/2])
	{
		puts("First"); fflush(stdout);
		while (1)
		{
			for (i=1;i<=n;++i) if (a[i]>0)
			{
				printf("%d\n",i); fflush(stdout);
				if (scanf("%d",&j),!j) return 0;
				int k=min(a[i],a[j]); a[i]-=k; a[j]-=k; break;
			}
		}
	} else
	{
		for (sum>>=1,i=n;i>=1;--i)
		if (g[i][sum]) tp[i]=1,sum-=a[i];
		puts("Second"); fflush(stdout);
		while (1)
		{
			if (scanf("%d",&j),!j) return 0;
			for (i=1;i<=n;++i) if (a[i]>0&&tp[i]!=tp[j])
			{
				printf("%d\n",i); fflush(stdout);
				int k=min(a[i],a[j]); a[i]-=k; a[j]-=k; break;
			}
		}
	}
	return 0;
}

Postscript

现在再想是不是要再搞一个小号接着打Div2,还是去打Div1遭受折磨

因为感觉以现在的水平时而连Div2D都做不出来,去打Div1也是纯被虐

不过如果不打Div1的话不知道多久才有机会突破到橙名,也算是有点两难的说

不过话说回来,立个flag,暑假结束前打到浅橙/深橙,这样感觉明年去打区域赛心里才稍微有点底的说

标签:const,876,scanf,Codeforces,int,Div,include,RI,define
From: https://www.cnblogs.com/cjjsb/p/17455998.html

相关文章

  • Codeforces Round 876 (Div. 2)题解
    CodeforcesRound876(Div.2)A.TheGoodArray标签greedymath题意对于任意\(i\in\{1,2,\dots,n\}\),要求数列\(a\)满足前\(i\)个元素中至少有\(\lceil\frac{i}{k}\rceil\)个元素为\(1\),后\(i\)个元素中至少有\(\lceil\frac{i}{k}\rceil\)个元素为\(1\)。思......
  • 2023.5 codeforces 杂题选做
    2023.5codeforces杂题选做E.Location\(n\le5\times10^4\)的范围,并且区间赋值、区间维护最值,可以考虑分块。然后对于散块的修改可以直接暴力,对于整块,由于\(b_i\)值不变,\(a_i\)值只有一个,思考如何快速求。由于\(\dfrac{\operatorname{lcm}(a_i,b_i)}{\gcd(a_i,b_i)}=\d......
  • 【LGR-141-Div.2】洛谷 6 月月赛 I (前两题)
    T1:金盏花传送门直接暴力枚举前6位的值即可,记得开longlong#include<bits/stdc++.h>usingnamespacestd;#defineintlonglonginty,z,ans=1e18;signedmain(){ scanf("%lld%lld",&y,&z); for(inti=100000;i<=999999;i++){ intx=i*1000000+y; ans=min......
  • P1545 [USACO04DEC] Dividing the Path G 题解
    丢一发好理解又好写的线段树优化dp。题目传送门简要题意给定一个长为\(l\)的线段,求出尽量少的不相交区间覆盖整段线段,要求题目给的所有子区间只被\(1\)个区间覆盖。分析显然题目给的子区间\([s,e]\)中只有\(s\)和\(e\)端点能作为线段端点,所以我们应该给\([s+1,......
  • Codeforces 1833E Round Dance
    看到shortestpaths来做的,但是好像没啥关系也没啥难度。首先能知道一个连通块肯定一次就能跳完,所以可以把连通块缩出来。然后有一个性质,记\(cz_i\)为\(i\)连通块内点种通过已知边推出的度数为\(1\)的个数为\(cz_i\),则\(cz_i\bmod2=0\)。记点\(i\)通过已知边推出......
  • codeforces Connected Components(寻找补图的连通块)
    http://codeforces.com/contest/920/problem/EE.ConnectedComponents?timelimitpertestmemorylimitpertestinputoutputn verticesand  edges.Insteadof......
  • https://blog.csdn.net/weixin_58018769/article/details/130380746
     :move="onMove" onMove(e){console.log(e);letmoveName=e.draggedContext.element.name//这个是当前拖拽的控件名//接下来判断该控件要不要拖进指定的容器if(moveName=='禁止拖拽进容器的控件名'&&e.to._prevClass=='要拖进的容器的class名......
  • Codeforces 1515I - Phoenix and Diamonds(值域倍增+线段树)
    首先\(c\)很大,因此复杂度跟\(c\)有关的项肯定只能是\(\logc\)之类的。类比IOI2021dungeons的套路,我们对值域进行分层,假设\(c\in[2^{\omega-1},2^{\omega})\),考虑令重量在\(\ge2^{\omega-1}\)的物品为“重物品”,其他物品为“轻物品”,那么一个显然的性质是我们最多只......
  • Codeforces Beta Round #2--B题 (DP)
    题目:Theleastroundway 1000*1000的方阵,每个格子有一个非负整数,现在要从左上走到右下,每次只能向下或者向右走。目标是使得所有走的格子里的数的乘积里,末尾0的个数最少,要求输出最有解和走法。不用怎么想也知道基本是个dp了,可以发现其实只有2和5的因子是有用的,但是如果状态同时记......
  • Codeforces Round 875 (Div. 2)B-D
    原题链接:https://codeforces.com/contest/1831原文:https://www.cnblogs.com/edgrass/p/17440602.html(B)Arraymerging主体思想是找到ab数组的最长相同字串(c中操作可实现连续)1#include<bits/stdc++.h>2usingnamespacestd;3intmain(){4intt;5cin>>......