首页 > 其他分享 >Codeforces Round #848 (Div. 2)(持续更新)

Codeforces Round #848 (Div. 2)(持续更新)

时间:2023-02-02 21:57:12浏览次数:60  
标签:CI const int Codeforces 848 return inline Div RI

Preface

这场打的可以说发挥了水平但没有完全发挥的说

ABC都是一眼一遍过,结果D是我最怕的期望题,大概推了个DP的式子但感觉成环了初始条件又不够(flag)就弃了

本来当时看E只有十几个人过的,抱着随便看看的心态看了眼题面发现是个套路题

结果30minRush完代码后一直连着T了好多发,直到比赛结束前两分钟才卡常卡过去

嘛不过不管怎么说应该不会掉分的说(最近两场的分怎么被CF给吃掉了,还没更新上去就难绷)


A. Flip Flop Sum

SB题,直接按题意枚举即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,a[N],pfx[N],suf[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 (pfx[0]=0,i=1;i<=n;++i) pfx[i]=pfx[i-1]+a[i];
		for (suf[n+1]=0,i=n;i>=1;--i) suf[i]=suf[i+1]+a[i];
		int ans=-1e9; for (i=1;i<n;++i) ans=max(ans,pfx[i-1]+suf[i+2]-a[i]-a[i+1]);
		printf("%d\n",ans);
	}
	return 0;
}

B. The Forbidden Permutation

SB题,话说这题题面写的很难懂赛后被好多人喷来着

不难发现由于只要有一个位置不合法即可,我们先特判掉答案为\(0\)的情况

然后考虑直接枚举每个位置,分别考虑破坏不等式两边所需要的最小步数即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,INF=1e9;
int t,n,m,d,p[N],pos[N],a[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d%d",&n,&m,&d),i=1;i<=n;++i)
		scanf("%d",&p[i]),pos[p[i]]=i;
		for (i=1;i<=m;++i) scanf("%d",&a[i]);
		bool flag=0; int ans=INF; for (i=1;i<m;++i)
		{
			int x=pos[a[i]],y=pos[a[i+1]];
			if (y<x||y>x+d) { flag=1; break; }
			ans=min(ans,y-x); if (n-1>d) ans=min(ans,d+1-(y-x));			
		}
		if (flag) puts("0"); else printf("%d\n",ans);
	}
	return 0;
}

C. Flexible String

爆搜题

不难发现关于答案的计算方式,我们只要考虑找出每段连续的两个串对应位置相同的区间长度即可

然后注意到题目中反复提到的字符集大小小于等于\(10\)的限制,可以直接爆搜改了哪些字符然后check即可

复杂度\(O(2^{10}\times n)\),实际情况前面是\(C_{10}^k\)只会更小

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,k,tot,rst[26]; char a[N],b[N]; bool vis[N],cs[15]; 
vector <int> pos[15]; long long ans;
inline long long C2(CI x)
{
	return 1LL*x*(x+1)/2LL;
}
inline void DFS(CI now=1,CI num=0)
{
	if (num>k) return; if (now==tot+1)
	{
		RI i; for (i=1;i<=n;++i) vis[i]=a[i]==b[i];
		for (i=1;i<=tot;++i) if (cs[i])
		for (int x:pos[i]) vis[x]=1;
		long long ret=0; int lst=0;
		for (i=1;i<=n;++i) if (!vis[i]) ret+=C2(i-1-lst),lst=i;
		ret+=C2(n-lst); ans=max(ans,ret); return;
	}
	cs[now]=1; DFS(now+1,num+1); cs[now]=0; DFS(now+1,num);
}
int main()	
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (tot=0,i=0;i<26;++i) rst[i]=0;
		for (i=0;i<15;++i) pos[i].clear();
		for (scanf("%d%d%s%s",&n,&k,a+1,b+1),i=1;i<=n;++i)
		{
			if (!rst[a[i]-'a']) rst[a[i]-'a']=++tot;
			pos[rst[a[i]-'a']].push_back(i);
		}
		ans=0; DFS(); printf("%lld\n",ans);
	}
	return 0;
}

D. Flexible String Revisit

DP题,方程很好写但是求解需要技巧

不难发现我们只关心两个串不同的位置的个数时多少,因此直接设\(f_{i}\)表示有\(i\)个不同位置的期望步数

转移方程显然有:\(f_i=f_{i+1}\times\frac{n-i}{n}+f_{i-1}\times \frac{i}{n}+1\)

即我们对当前状态进行操作,有\(\frac{i}{n}\)的概率选中一个不同的位,这样还要操作\(f_{i-1}\)次,反之则有\(\frac{n-i}{n}\)的概率还要操作\(f_{i+1}\)次

然后这个DP看似是成环的,当时我只发现了\(f_0=0\)这一个终止条件,但没有注意到\(f_n=f_{n-1}+1\)这个特殊情况

有了这个边界之后我们就可以考虑解方程了,因为那个转移的式子可以写成:

\[f_{i-1}=f_{i}\times\frac{n-i+1}{n}+f_{i-2}\times \frac{i-1}{n}+1\\ \Leftrightarrow f_i=(f_{i-1}-1-f_{i-2}\times \frac{i-1}{n})\times \frac{n}{n-i+1} \]

这是一个关于前两项的递推式,而根据我们的两个已知条件式可以解出来的

我们不妨设\(f_1\)是已知的初始条件,最后我们可以把每个\(f_i\)都用一个二元组\((x,y)\)来表示,其中\(f_i=x\times f_1+y\)

这样我们在得到了\(f_n,f_{n-1}\)的表示后可以反解出\(f_1\)的值,从而推出其中的任意一项了

不得不说这个trick之前没有见过,算是长见识了的说

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1000005,mod=998244353;
inline int sum(CI x,CI y)
{
	return x+y>=mod?x+y-mod:x+y;
}
inline int sub(CI x,CI y)
{
	return x-y<0?x-y+mod:x-y;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
struct Data // x*f[1]+y
{
	int x,y;
	inline Data(CI X=0,CI Y=0)
	{
		x=X; y=Y;
	}
	friend inline Data operator + (const Data& A,const Data& B)
	{
		return Data(sum(A.x,B.x),sum(A.y,B.y));
	}
	friend inline Data operator - (const Data& A,const Data& B)
	{
		return Data(sub(A.x,B.x),sub(A.y,B.y));
	}
	friend inline Data operator * (const Data& A,const Data& B)
	{
		return Data(1LL*A.x*B.x%mod,1LL*A.y*B.y%mod);
	}
}f[N]; int t,n,invn,dif; char 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%s%s",&n,a+1,b+1),dif=0,i=1;i<=n;++i) dif+=a[i]!=b[i];
		for (f[0]=Data(0,0),f[1]=Data(1,0),invn=quick_pow(n),i=2;i<=n;++i)
		{
			int tmp=1LL*(i-1)*invn%mod,div=quick_pow(sub(1,tmp));
			f[i]=(f[i-1]-Data(0,1)-(f[i-2]*Data(tmp,tmp)))*Data(div,div);
		}
		Data tmp=f[n]-f[n-1]; int f1=1LL*sub(1,tmp.y)*quick_pow(tmp.x)%mod;
		printf("%d\n",sum(1LL*f[dif].x*f1%mod,f[dif].y));
	}
	return 0;
}

E. The Tree Has Fallen!

套路题

一堆数里选一些出来求最大的异或和,这个一眼线性基没跑了

换根操作的话一眼先定根然后再分类讨论,顺着这个思路这题就很简单了

首先假定\(1\)为根,然后我们考虑在这个有根树上把根换成\(r\)对于\(v\)的影响:

  • 当\(r\)不在\(v\)的子树中时,不难发现此时我们要找答案的数集就还是\(v\)的子树
  • 当\(r\)在\(v\)的子树中时,手玩一下我们发现,设\(w\)为\(v\)的直接儿子,并且满足\(r\)在\(w\)的子树中,则此时我们要找答案的数集就是所有数的集合减去\(w\)的子树

因此我们把数的DFS序求出来,这样就变成了区间询问的题目了,由于线性基支持合并,因此直接上线段树即可

但是由于\(O(n\log^2 n)\)的复杂度且由于我各部分的封装实现,常数巨大,卡了好久的常数才跑过

后来比赛结束后躺在床上转念一想由于只有查询操作,且两个区间合并时有重复不会影响答案(因为重复的部分相当于插入了两次线性基,对答案没有影响),因此可以用ST表来写

不过由于我太懒的说既然卡过了就万事大吉了,就懒得再写了

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,P=30;
int t,n,a[N],x,y,q,dep[N],dfn[N],L[N],R[N],tot; vector <int> v[N];
struct LB
{
	int v[P];
	inline LB(void) { memset(v,0,sizeof(v)); }
	inline void clear(void) { memset(v,0,sizeof(v)); }
	inline void insert(int x)
	{
		for (RI i=P-1;~i;--i) if ((x>>i)&1)
		{
			if (!v[i]) return (void)(v[i]=x); x^=v[i];
		}
	}
	inline int query(int x=0)
	{
		for (RI i=P-1;~i;--i) if ((x^v[i])>x) x^=v[i]; return x;
	}
	friend inline LB operator + (const LB& A,const LB& B)
	{
		LB C; RI i; for (i=0;i<P;++i) C.v[i]=A.v[i];
		for (i=0;i<P;++i) if (B.v[i]) C.insert(B.v[i]); return C;
	}
};
class Segment_Tree
{
	private:
		LB tr[N<<2];
		#define TN CI now=1,CI l=1,CI r=n
		#define LS now<<1,l,mid
		#define RS now<<1|1,mid+1,r
		inline void pushup(CI now)
		{
			tr[now]=tr[now<<1]+tr[now<<1|1];
		}
	public:
		inline void build(TN)
		{
			if (l==r) return tr[now].clear(),tr[now].insert(a[dfn[l]]);
			int mid=l+r>>1; build(LS); build(RS); pushup(now);
		}
		inline LB query(CI beg,CI end,TN)
		{
			if (beg>end) return LB(); if (beg<=l&&r<=end) return tr[now]; int mid=l+r>>1;
			if (beg<=mid&&end<=mid) return query(beg,end,LS);
			if (end>mid&&beg>mid) return query(beg,end,RS);
			return query(beg,end,LS)+query(beg,end,RS);
		}
		#undef TN
		#undef LS
		#undef RS
}SEG;
class Tree_Solver
{
	private:
		int anc[N][20];
	public:
		inline void DFS(CI now=1,CI fa=0)
		{
			RI i; for (i=0;i<20;++i) anc[now][i]=0;
			dep[now]=dep[anc[now][0]=fa]+1; dfn[++tot]=now; L[now]=tot;
			for (i=0;i<19;++i) if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break;
			for (int to:v[now]) if (to!=fa) DFS(to,now); R[now]=tot;
		}
		inline void jump(int& x,CI y)
		{
			for (RI i=20;~i;--i) if ((y>>i)&1) x=anc[x][i];
		}
}T;
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 (tot=0,T.DFS(),SEG.build(),scanf("%d",&q),i=1;i<=q;++i)
		{
			if (scanf("%d%d",&x,&y),x==y) printf("%d\n",SEG.query(1,n).query()); else
			{
				if (L[y]<=L[x]&&L[x]<=R[y])
				{
					T.jump(x,dep[x]-dep[y]-1);
					printf("%d\n",(SEG.query(1,L[x]-1)+SEG.query(R[x]+1,n)).query());
				} else printf("%d\n",SEG.query(L[y],R[y]).query());
			}
		}
	}
	return 0;
}

标签:CI,const,int,Codeforces,848,return,inline,Div,RI
From: https://www.cnblogs.com/cjjsb/p/17087534.html

相关文章

  • Codeforces Round #845 (Div. 2) and ByteRace 2023(A,B,C)
    CodeforcesRound#845(Div.2)andByteRace2023(A,B,C)AA这个题目大意是有n个数,我们需要让ai和ai+1的奇偶性不一样我们可以让相邻的两个数变成一个数,那个数就是这两个......
  • 2.2 vp Codeforces Round #848 (Div. 2)
    A-FlipFlopSum题意给出长度为n的序列a,a中只包括1和-1,你必须操作一次,让相邻两个元素由1变-1或由-1变1,问操作后数组总和最大多少思路暴力即可voidsolve(){ intn......
  • Codeforces Round #848 (Div. 2)
    题目链接A核心思路按照题目模拟就好了//Problem:A.FlipFlopSum//Contest:Codeforces-CodeforcesRound#848(Div.2)//URL:https://codeforces.com/cont......
  • Codeforces Round #831 (Div. 1 + Div. 2) A-H
    CodeforcesRound#831(Div.1+Div.2)A-H题解好久之前写的,发现忘记发了比赛题目质量高,推荐!传送门CodeforcesRound#831(Div.1+Div.2)A.FactoriseN+M题......
  • Codeforces Round #848 (Div. 2) A~C
    A.给出一个-1,1序列,操作是交换相邻位置的符号,一定要操作一次,求最大sum。只有4种情况,扫一遍判断是否出现即可。B题面写得真清楚啊:)#include<bits/stdc++.h>usingnam......
  • Codeforces Round #848 (Div. 2) A~F 题解
    A.FlipFlopSum能换\(-1,-1\)就换,不能能换\(1,-1\)或\(-1,1\)也可以,否则只能换\(1,1\)。B.TheForbiddenPermutation如果原序列一开始就是好的那就结束,否则......
  • Codeforces1778E 【线性基】【换根DP】
    我,差30秒,就能AC这道题,我知道错哪的时候是倒计时30,不记得优先级想赌一把,因为我没有先写括号的习惯,所以倒计时14的时候交了,然后想改了括号再交一发,改了括号之后,比赛结束了。......
  • Educational Codeforces Round 135 (Rated for Div. 2) F. Fishermen 二分图最小带权
    不用真的建图,真的建图两人之间的代价不好算。等价转化为对给定的ai找出bi,使得bi=k*a[i],且互不相同k的上界为n,简易证明:[若a[i]互不相等,全部选a[i]*n会比a[i]*(n+1)更好;......
  • 2.1 vp Codeforces Round #842 (Div. 2)
    A-GreatestConvex题意给出k,要找出最大的x(1<=x<=k),使x!+(x-1)!是k的倍数,问是否存在,为多少思路变换一下即可得原式为(x-1)!(x+1),若要满足条件,令x=k-......
  • Educational Codeforces Round 134 (Rated for Div. 2) F - Matching Reduction 最小
    真傻逼Σ(☉▽☉"a——wa23是因为数组开小了,wa53是数组开大了爆了(不能任性随便开了问最少删多少点使得最大匹配数-1这题就考一个结论:最大匹配数==最小点覆盖 所以很......