【题解】AtCoder Regular Contest 163 A-D

E 太过于 adhoc,F 太过于神仙,就不做了。

A.Divide String

题目描述:

多组数据。

给出一个长为 N 的字符串,问能否将其划分为多段,使字典序严格上升,保证 N2000

2  N  2000

题目分析:

考虑如果划分为三段,使得其满足 S1<S2<S3,则必然满足 S1<S2+S3
所以我们完全没必要划分为三段及以上,划分为两段就足够了。
可以直接枚举划分的分界点,然后 O(n) 判断是否合法。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2005;
char s[N];
bool chk(int l,int mid,int r){
	int len1 = mid - l + 1;
	int len2 = r - (mid+1) + 1;
	int posl = l,posr = mid+1;
	while(posl <= mid && posr <= r && s[posl] == s[posr]){
		++posl,++posr;
	}
	if(posl > mid || posr > r)	return len1 < len2;
	return s[posl] < s[posr];	
}
int main(){
	int T;scanf("%d",&T);
	while(T--){
		int n;scanf("%d",&n);
		scanf("%s",s+1);
		bool flag = false;
		for(int i=1; i<n; i++){
			if(chk(1,i,n)){
				flag = true;
				break;
			}
		}
		if(flag)	printf("Yes\n");
		else	printf("No\n");
	}
	return 0;
}

B.Favorite Game

题目描述:

给定 N,M 和数列 AN。定义一次操作为任取一个 Ai(1iN) 加一或减一。求经过几次操作后可以使得至少有 Mi(3iN) 满足 A1AiA2

3  N  2 × 1051  M  N21  Ai  109

题目分析:

首先我们一定是只会操作 a1,a2,因为操作 a1,a2 相当于对其余值整体的一个操作,这样做显然优于只操作一个。
最后满足条件的数一定在 a 排序后的数组上是一个连续的区间,所以可以直接枚举这个区间是什么,设左右端点分别为 al,ar
则我们就是要通过操作让 a1ala2ar,这个就可以随便做了。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+5;
const int INF = 1e11+5;
vector<int> v1,v2;
int a[N],n,m;
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(int i=1; i<=n; i++)	scanf("%lld",&a[i]);
	int tmp = INF;
	int l = a[1],r = a[2];
	sort(a+3,a+n+1);
	for(int i=3; i + m - 1<=n; i++){
		int res = 0;
		if(l > a[i])	res += l - a[i];
		if(r < a[i + m - 1])	res += a[i + m - 1] - r;
		tmp = min(tmp,res);
	}
	printf("%lld\n",tmp);
	return 0;
}

C.Harmonic Mean

题目描述:

给定正整数 N,你需要构造一个正整数序列 Ai,满足:

  • i=1n1Ai=1
  • Ai 互不相同;
  • 1Ai109

多组数据,1T,N500

题目分析:

看到这个 1Ai 一个能想到的东西就是 1k(k+1)=1k1k+1
会发现这个相当于一个裂项的形式,也就是我们可以直接:

11×2+12×3+23×4++1(n1)×n+1n=11n+1n=1

这个时候发现其实就基本能构造出来了,就是当 n=k×(k+1) 的时候这种做法就寄了。
但是如果 n 满足这个形式,则 n1 一定不满足这个形式,所以我们可以按 n1 构造出一种方案,然后将所有数乘 2 后再加上数 2 就好了。
其实就是让 i=1n11Bi=12 然后再加 12 就好了。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
vector<int> v;
void solve(int n){
	for(int i=1; i<=n-1; i++)	v.push_back(i * (i + 1));
	v.push_back(n);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%d",&T);
	while(T--){
		int n;scanf("%d",&n);
		if(n == 2){
			printf("No\n");
			continue;
		}
		bool flag = false;
		for(int i=1; i<=n; i++){
			if(i * (i + 1) == n)	flag = true;
		}
		if(flag){
			solve(n-1);
			for(int i=0; i<v.size(); i++)	v[i] *= 2;
			v.push_back(2);
		}
		else	solve(n);
		printf("Yes\n");
		for(auto i : v)	printf("%d ",i);
		v.clear();
		printf("\n");
	}
	return 0;
}

D.Sum of SCC

题目描述:

考虑一张竞赛图 G,其中有 N 个节点,节点编号为 1,2,,N,且 G 满足:

  • 对于 G 中的所有边 uv,恰好有 M 条边满足 u<v

f(G) 表示图 G 中的强连通分量数量。请你求出所有满足条件的 Gf(G) 之和。

答案对 998244353 取模。

1N300MN(N1)2

题目分析:

之前不大知道竞赛图的性质,下面稍微罗列一下:
性质一:竞赛图缩点之后成链状。
注意这里不是一条链而是链状,就如下图所示:

性质二:竞赛图的每一个强连通分量必然存在哈密顿回路
性质三:竞赛图必然存在哈密顿回路

因为缩点之后每一个点代表一个 SCC,所以可以推出下面这个结论:

一个竞赛图的 SCC 个数为将其点集划分为两个集合 A,B(可以为空)并满足以下限制的方案数减 1
对于 uAvB 的边 (u,v),其方向必然为 uv

有了这个结论也就是说我们可以直接对于每一个点 dp 求其属于 A 还是 B,就是直接求这个限制下划分的方案数。
因为题目里要求我们的统计的存在一个大小关系,所以不让从小到大插入点,也就是设 dp[i][j][k] 表示插入了编号为 [1,i+j] 的点,|A|=i|B|=j,满足题目条件的边数为 k 的方案数。
转移的话就是枚举 i+j+1 这个点插入到 A 还是 B,如下:

x=0idp[i][j][k]×(xi)dp[i+1][j][k+x]x=0jdp[i][j][k]×(xj)dp[i][j+1][k+i+x]

考虑放在 A,对于跨集合的边必然是 i+j+1B 而因为 i+j+1 必然是最大值也就是说这样的连边一定不满足条件,也就不用管;对于在集合内部的边就是枚举多少个满足条件即可。
考虑放在 B,对于跨集合的边必然是 Ai+j+1,也就是一定满足条件,直接全加上就好;对于在集合内部的边就是枚举多少个满足条件即可。

也要注意最后把减 1 这个贡献也算上。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 35;
const int MOD = 998244353;
int f[N][N][N*N],C[N*N][N*N]; 
signed main(){
	int n,m;scanf("%lld%lld",&n,&m);
	C[0][0] = 1;
	for(int i=1; i<=n * n; i++){
		C[i][0] = 1;
		for(int j=1; j<=i; j++){
			C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
		}
	}
	f[0][0][0] = 1;
	for(int i=0; i<n; i++){
		for(int j=0; i + j < n; j++){
			for(int k=0; k<=m; k++){
				for(int x=0; x<=i; x++)	
					if(k + x <= m)	f[i+1][j][k+x] = (f[i+1][j][k+x] + f[i][j][k] * C[i][x])%MOD;
				for(int x=0; x<=j; x++)	
					if(k + i + x <= m)	f[i][j+1][k+i+x] = (f[i][j+1][k+i+x] + f[i][j][k] * C[j][x])%MOD;
			}
		}
	}
	int ans = 0;
	for(int i=0; i<=n; i++)	ans = (ans + f[i][n-i][m])%MOD;
	ans = (ans - C[n * (n-1) / 2][m] + MOD)%MOD;   //最后的 -1 产生的贡献 
	printf("%lld\n",ans);
	return 0;
}
posted @   linyihdfj  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示