Hello 2023(持续更新)

Preface

2023的First Round的说,虽然犯了很多脑抽错误但幸好还是堪堪上了点分

比赛时写出了ABCD,E结束前3min刚写完结果挂PT6了,今天稍微改了下就过了

嘛不管怎样还是跌跌撞撞回到1800+了,可以再次开始冲击紫名了


A. Hall of Fame

比赛的时候不知道为什么脑抽了想复杂了,然后10min的时候才堪堪做掉

我的思路就是设第一个出现的R的下标为\(x\),最后一个出现的L的下标为\(y\),若\(x<y\)则有解

首先判断掉不用交换就合法的情况,然后考虑交换的只可能是相邻的LR,在交换的时候维护下新的\(x,y\)来判断即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,L1,R1; char s[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%s",&n,s+1); L1=0; R1=n+1;
		for (i=1;i<=n;++i) if (s[i]=='R')
		{
			if (R1==n+1) R1=i; else break;
		}
		for (i=n;i>=1;--i) if (s[i]=='L')
		{
			if (!L1) L1=i; else break;
		}
		if (R1<L1) { puts("0"); continue; }
		bool flag=0; for (i=1;i<n&&!flag;++i) if (s[i]!=s[i+1])
		{
			if (s[i]=='L')
			{
				int LL=L1,RR=R1;
				if (i==LL) ++LL; if (i+1==RR) --RR;
				if (RR<LL) { printf("%d\n",i); flag=1; }
			} 
		}
		if (!flag) puts("-1");
	}
	return 0;
}

B. MKnez's ConstructiveForces Task

首先一眼偶数随便构造,直接\(1,-1,1,-1,\cdots,1,-1\)这样即可

然后奇数第一眼以为是无解的,后面细想了一下还是可以构造\(n>3\)的情况的

首先不难发现所有奇数位置上的数都要相同,且所有偶数位置上的数也要相同,否则不合法

\(\sum_{i=1}^n s_i=t\),我们把所有的\(n-1\)对相邻的数相加,则可以列出式子\((n-1)t=2t-(s_1+s_n)\)

因此\(s_1=s_n=\frac{-(n-3)t}{2}\),然后我们不妨设\(t=-1\)即可得到一种构造方案

比如\(n=5\)时有

1 -2 1 -2 1

\(n=7\)时有

2 -3 2 -3 2 -3 2

依此类推即可

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

C. Least Prefix Sum

我真是傻逼竟然会认为直接贪心是对的,WA了两发不说还浪费好多时间,真想给自己两个大耳光子

首先我们令\(pfx_i=\sum_{j=1}^i a_j\),把\(pfx_k\ge pfx_m\)的条件转化一下:

  • \(k<m\),则\(pfx_k\ge pfx_m\Leftrightarrow \sum_{i=k+1}^m a_i\le 0\)
  • \(k>m\),则\(pfx_k\ge pfx_m\Leftrightarrow \sum_{i=m+1}^k a_i\ge 0\)

因此我们分别处理两边的情况,刚开始我naive地认为在所有不合法的地方进行反转操作即可

后来稍加思索造了个数据把自己卡了

5 1
0 10 -9 -8 -10

遂发现这是个最简单的带悔贪心的题,我们把所有没反转过的数扔到堆里,在未来选择反悔选它的时候贡献就是\(-2\times a_i\)

每次需要反转的时候找一个最优的出来即可,总复杂度\(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<queue>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,m,a[N],ans; priority_queue <int> hp;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
		while (!hp.empty()) hp.pop();
		long long cur=0; for (ans=0,i=m;i>=2;--i)
		if (hp.push(2*a[i]),cur+a[i]>0)
		++ans,cur+=-hp.top()+a[i],hp.pop(); else cur+=a[i];
		while (!hp.empty()) hp.pop();
		for (cur=0,i=m+1;i<=n;++i)
		if (hp.push(-2*a[i]),cur+a[i]<0)
		++ans,cur+=hp.top()+a[i],hp.pop(); else cur+=a[i];
		printf("%d\n",ans);
	}
	return 0;
}

D. Boris and His Amazing Haircut

这题确实很naive了,随便想想都知道怎么做,还好C过完之后心态没爆炸很稳地一遍写过,不然铁掉分

首先考虑特判掉\(a_i<b_i\)的情况,然后我们发现我们可以按从小到大的顺序考虑所有\(b_i\)

即我们先把所有数离散化,然后枚举一个在\(b_i\)中未被处理掉的最小的数\(t\)

考虑找出所有\(b_i=t\and a_i\ne t\)的下标序列\(s_1,s_2,\cdots,s_m\)

刚开始naive地认为只要找到\(s_1,s_2,\cdots,s_m\)一共可由多少个区间覆盖,把这个值和\(x_i\)\(t\)的个数做比较即可

但是后来发现所有之前已经被处理掉的位置会起到一个联通两端的作用

比如我们要把3 3 3变成2 1 2,在用一把值为1的锥子后我们把下标为\(2\)的位置处理掉了,这样位置\(1\)和位置\(3\)其实就相当于可以放在一起处理了,我们再用一把值为2的锥子把\([1,3]\)都推了即可

因此我们再用一个双向列表来维护下每个数左右第一个没被处理掉的位置是什么即可

总体实现起来由于要排序,因此复杂度为\(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=800005;
int t,n,m,a[N],b[N],rst[N],cnt,L[N],R[N],x[N],num[N],tp[N],tot; vector <int> pos[N];
inline void remove(CI x)
{
	int pre=L[x],nxt=R[x]; R[pre]=nxt; L[nxt]=pre;
}
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),cnt=0,i=1;i<=n;++i) scanf("%d",&a[i]),rst[++cnt]=a[i];
		for (i=1;i<=n;++i) scanf("%d",&b[i]),rst[++cnt]=b[i];
		for (scanf("%d",&m),i=1;i<=m;++i) scanf("%d",&x[i]),rst[++cnt]=x[i];
		bool flag=1; for (i=1;i<=n&&flag;++i) if (a[i]<b[i]) flag=0;
		if (!flag) { puts("NO"); continue; }
		sort(rst+1,rst+cnt+1); cnt=unique(rst+1,rst+cnt+1)-rst-1;
		for (i=1;i<=n;++i) a[i]=lower_bound(rst+1,rst+cnt+1,a[i])-rst;
		for (i=1;i<=n;++i) b[i]=lower_bound(rst+1,rst+cnt+1,b[i])-rst;
		for (i=1;i<=cnt;++i) pos[i].clear(),num[i]=0;
		for (i=1;i<=m;++i) ++num[x[i]=lower_bound(rst+1,rst+cnt+1,x[i])-rst];
		for (i=1;i<=n;++i) L[i]=i-1,R[i]=i+1,pos[b[i]].push_back(i);
		for (i=1;i<=cnt&&flag;++i) if (pos[i].size())
		{
			tot=0; for (int t:pos[i]) if (a[t]==b[t]) remove(t); else tp[++tot]=t;
			if (!tot) continue;	int cur=1; for (j=1;j<tot;++j)
			if (R[tp[j]]!=tp[j+1]) ++cur;
			if (cur>num[i]) flag=0; for (j=1;j<=tot;++j) remove(tp[j]);
		}
		puts(flag?"YES":"NO");
	}
	return 0;
}

E. Anya's Simultaneous Exhibition

比赛的时候连蒙带猜基本把方法搞出来了,结果写的时候一个下标搞错了多了\(1\),可惜差点完成2min绝杀

考虑若\(x\)能战胜\(y\),我们就连一条\(x\to y\)的有向边,首先我们考虑用\(n\)次操作问出每个点的出度\(out_i\)

然后我们不难发现出度最多的点\(x\)一定可以获胜,考虑用反证法

因为一定会存在至少一个点获胜,而这个点不是\(x\),我们不妨将其设为\(y\),且满足\(out_x\ge out_y\)

那么我们不难推出\(x,y\)之间的边一定是\(y\to x\),否则\(x\)可以让\(y\)打败其它点后再打败他而获胜

依此类推对于所有\(x\)能打赢的点\(z\)\(y\)也必须要打赢\(z\),否则\(x\)可以让\(y\)打败其它点后再用\(z\)打败\(y\),再自己打败\(z\)获胜

因此我们发现所有\(x\)出度的点,也必是\(y\)的出度,并且由于\(y\to x\)这条边,我们可以推出\(out_x<out_y\)

这与\(out_x\)是出度最大的点矛盾,因此假设不成立

因此由这个思路我们不难想到一种利用归纳法的方法来判断每个点是否能获胜

我们把所有点按出度从大到小排序,假设我们已经确定了点\(1,2,\cdots,k\)可以获胜

如果点\(k+1,k+2,\cdots n\)到前\(k\)个点都没边,则最后的答案就是前\(k\)个点

否则我们可以找出后面的点中第一个到前\(k\)个点有边的点\(t\),则这个点必然也是可以获胜的

然后我们令\(k=t\),继续扩展可以获胜的点的集合的边界即可,不难发现这样操作最多只要再进行\(n-1\)次询问即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=255;
struct element
{
	int out,id;
	friend inline bool operator < (const element& A,const element& B)
	{
		return A.out>B.out;
	}
}a[N]; int n,ans[N],mark[N],lim;
int main()
{
	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
	{
		for (printf("? %d ",i),j=1;j<=n;++j)
		printf("%c",i!=j?'1':'0'); printf("\n");
		fflush(stdout); scanf("%d",&a[i].out); a[i].id=i;
	}
	for (lim=1,sort(a+1,a+n+1),i=2;i<=n;++i)
	{
		for (j=1;j<=n;++j) mark[j]=0;
		for (j=1;j<=lim;++j) mark[a[j].id]=1;
		for (printf("? %d ",a[i].id),j=1;j<=n;++j)
		printf("%c",mark[j]?'1':'0'); printf("\n");
		fflush(stdout); int tp; scanf("%d",&tp);
		if (tp) lim=i;
	}
	for (i=1;i<=lim;++i) ans[a[i].id]=1;
	for (printf("! "),i=1;i<=n;++i)
	printf("%c",ans[i]?'1':'0'); printf("\n");
	return fflush(stdout),0;
}
posted @ 2023-01-04 22:00  空気力学の詩  阅读(68)  评论(0编辑  收藏  举报