Codeforces Round 921 (Div. 1)

Preface

被折纸狠狠地腐乳了,但好在手速够快光速写完前两题成功把Ohara_Rinne这个号也打上橙了

之后就不开其它小号打了,也差不多该尝试去向上挑战了,不然一直呆在舒适圈内也没啥提升的说


A. Did We Get Everything Covered?

直接把序列自动机建出来,不妨设状态\((x,y)\)表示构造了长度为\(x\)的串,匹配到\(s\)中的位置为\(y\)

不难发现状态总数只有\(O(nm)\)级别,转移的时候再枚举下一个位置填什么字符即可,拿个BFS转移一下

#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=1005;
int t,n,k,m,nxt[N][30],bkt[N],pre[30][N],cs[30][N]; char s[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; scanf("%d%d%d%s",&n,&k,&m,s+1);
		for (i=0;i<k;++i) bkt[i]=m+1;
		for (i=m+1;i>=0;--i)
		{
			for (j=0;j<k;++j) nxt[i][j]=bkt[j];
			if (1<=i&&i<=m) bkt[s[i]-'a']=i;
		}
		for (i=0;i<=n;++i) for (j=0;j<=m+1;++j) pre[i][j]=-1;
		queue <pi> q; q.push(pi(0,0));
		while (!q.empty())
		{
			auto [len,pos]=q.front(); q.pop();
			if (len==n) continue;
			for (i=0;i<k;++i)
			{
				int npos=nxt[pos][i];
				if (pre[len+1][npos]==-1)
				pre[len+1][npos]=pos,cs[len+1][npos]=i,q.push(pi(len+1,npos));
			}
		}
		if (pre[n][m+1]==-1) { puts("YES"); continue; }
		string ans=""; for (int len=n,pos=m+1;len>0;pos=pre[len][pos],--len) ans+=cs[len][pos]+'a';
		reverse(ans.begin(),ans.end()); cout<<"NO"<<endl<<ans<<endl;
	}
	return 0;
}

B. Space Harbour

很套路的一个题,天天模拟赛写DS终于在CF上派上一点用场了

考虑答案的计算形式,我们要维护的是区间内\(val_{pre}\times dis_{nxt}\)之和,其中\(pre,nxt\)分布表示相对于该点的空间站的前驱(不包括本身)以及后继(包括本身)

稍微手玩一下会发现假设我们要在\(x\)处新建一个空间站,而在建立之前\(x\)的前驱和后继分别为\(l,r\),则:

  • 区间\([x,r-1]\)中的所有点对应的\(val\)系数都要变成\(val_x\),但赋值不好处理我们可以转化为区间加来做
  • 区间\([l+1,x]\)中的所有点对应的\(dis\)系数都要减去\(r-x\)

因此修改操作其实就是两种系数的区间加,那么我们要快速维护修改后的答案的话,只需要额外维护下区间内两种系数的和即可

找前驱后继可以用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 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=300005;
int n,m,q,a[N],b[N],v[N],tp,x,y; set <int> s;
inline int getpre(CI x)
{
	if (x==1) return 0; return *(--s.lower_bound(x));
}
inline int getnxt(CI x)
{
	return *s.lower_bound(x);
}
class Segment_Tree
{
	private:
		struct segment
		{
			int len,res,sum_val,sum_dis,tag_val,tag_dis;
		}O[N<<2];
		inline void pushup(CI now)
		{
			O[now].res=O[now<<1].res+O[now<<1|1].res;
			O[now].sum_val=O[now<<1].sum_val+O[now<<1|1].sum_val;
			O[now].sum_dis=O[now<<1].sum_dis+O[now<<1|1].sum_dis;
		}
		inline void apply_val(CI now,CI mv)
		{
			O[now].res+=O[now].sum_dis*mv;
			O[now].sum_val+=O[now].len*mv;
			O[now].tag_val+=mv;
		}
		inline void apply_dis(CI now,CI mv)
		{
			O[now].res+=O[now].sum_val*mv;
			O[now].sum_dis+=O[now].len*mv;
			O[now].tag_dis+=mv;
		}
		inline void pushdown(CI now)
		{
			if (O[now].tag_val) apply_val(now<<1,O[now].tag_val),apply_val(now<<1|1,O[now].tag_val),O[now].tag_val=0;
			if (O[now].tag_dis) apply_dis(now<<1,O[now].tag_dis),apply_dis(now<<1|1,O[now].tag_dis),O[now].tag_dis=0;
		}
	public:
		#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 build(TN)
		{
			O[now].len=r-l+1;
			if (l==r)
			{
				O[now].sum_val=v[getpre(l)];
				O[now].sum_dis=getnxt(l)-l;
				O[now].res=O[now].sum_val*O[now].sum_dis;
				return;
			}
			int mid=l+r>>1; build(LS); build(RS); pushup(now);
		}
		inline void modify_val(CI beg,CI end,CI mv,TN)
		{
			if (beg<=l&&r<=end) return apply_val(now,mv); int mid=l+r>>1; pushdown(now);
			if (beg<=mid) modify_val(beg,end,mv,LS); if (end>mid) modify_val(beg,end,mv,RS); pushup(now);
		}
		inline void modify_dis(CI beg,CI end,CI mv,TN)
		{
			if (beg<=l&&r<=end) return apply_dis(now,mv); int mid=l+r>>1; pushdown(now);
			if (beg<=mid) modify_dis(beg,end,mv,LS); if (end>mid) modify_dis(beg,end,mv,RS); pushup(now);
		}
		inline int query(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return O[now].res; int mid=l+r>>1,ret=0; pushdown(now);
			if (beg<=mid) ret+=query(beg,end,LS); if (end>mid) ret+=query(beg,end,RS); return ret;
		}
		#undef TN
		#undef LS
		#undef RS
}SEG;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%lld%lld%lld",&n,&m,&q),i=1;i<=m;++i) scanf("%lld",&a[i]);
	for (i=1;i<=m;++i) scanf("%lld",&b[i]),v[a[i]]=b[i];
	for (i=1;i<=m;++i) s.insert(a[i]);
	for (SEG.build(),i=1;i<=q;++i)
	{
		scanf("%lld%lld%lld",&tp,&x,&y);
		if (tp==1)
		{
			int l=getpre(x),r=getnxt(x); v[x]=y; s.insert(x);
			if (x<=r-1) SEG.modify_val(x,r-1,y-v[l]);
			if (l+1<=x) SEG.modify_dis(l+1,x,x-r);
		} else printf("%lld\n",SEG.query(x,y));
	}
	return 0;
}

C. Fractal Origami

唉只能说这种题对于我这种脑子不好的人来说就是绝杀,要是组队训练的时候遇到我绝对看一眼就丢给队友的说

这题主要要发现两个trick点,首先是除了第一次折叠外,后面的每一次折叠的都是偶数层,因此每次新产生的山峰和山谷的长度是相等的

然后每次新产生的折痕长度相较于上次,边长是原来的\(\frac{1}{\sqrt 2}\),但数量翻倍了,因此折痕的总长度是上一步的\(\sqrt 2\)

不妨设\(S=V+M=\sum_{i=1}^ n 2\sqrt 2\times (\sqrt 2)^{i-1},D=V-M=2\sqrt 2\),则\(\frac{M}{V}=\frac{S-D}{S+D}\)

根据\(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 mod=999999893;
int t,n;
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;
}
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d",&n); int A,B,C,D;
		if (n%2==0)
		{
			int x=(quick_pow(2,n/2)-1+mod)%mod;
			A=C=4LL*x%mod; B=(2LL*x-2+mod)%mod; D=(2LL*x+2)%mod;
		} else
		{
			int x=4LL*quick_pow(2,(n-1)/2)%mod;
			A=C=(x-4+mod)%mod; B=(x-4+mod)%mod; D=x;
		}
		printf("%d\n",1LL*(1LL*B*C%mod-1LL*A*D%mod+mod)*quick_pow((1LL*C*C%mod-2LL*D*D%mod+mod)%mod)%mod);
	}
	return 0;
}

D. Balanced Subsequences

最神奇的一题,感觉做法不难而且很典,为什么就是想不到呢

首先考虑容斥,设\(f(n,m,k)\)表示用\(n\)(\(m\))组成的最长合法括号子序列长度不超过\(2k\)的方案数,那么最后的答案就是\(f(n,m,k)-f(n,m,k-1)\)

\(k\ge \min(n,m)\)时,此时不管怎么放都是合法的,因此\(f(n,m,k)=C_{n+m}^n\)

否则\(f(n,m,k)=f(n-1,m,k-1)+f(n,m-1,k)\),前者代表第一个位置放(,后者表示第一个位置放)

注意因为此时\(k<\min(n,m)\),因此第一个位置放(后,后面不管怎么放一定会多出至少一个),因此总能匹配上

然后根据数学归纳法以及一些边界条件,可以直接推出\(f(n,m,k)=C_{n+m}^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=4005,mod=1e9+7;
int t,n,m,k,fact[N],ifac[N];
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;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
	if (n<0||m<0||n<m) return 0;
	return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod;
}
inline int f(CI n,CI m,CI k)
{
	if (k>=min(n,m)) return C(n+m,n); else return C(n+m,k);
}
int main()
{
	for (scanf("%d",&t),init(4000);t;--t)
	scanf("%d%d%d",&n,&m,&k),printf("%d\n",(f(n,m,k)-f(n,m,k-1)+mod)%mod);
	return 0;
}

Postscript

接下来好像很长一段时间没有CF了,算是终于能迎来一段小憩的时间了

不存在的还有之前的一些CF和ATC没补,有空还是多写写题吧

posted @ 2024-01-31 20:07  空気力学の詩  阅读(50)  评论(0编辑  收藏  举报