AtCoder Beginner Contest 242 C~E,G题解

AtCoder Beginner Contest 242

C

题意:求出满足下列条件的序列的方案数。

  1. 长度为 n n n.
  2. 对于任意的 i i i, 1 ≤ x i ≤ 9 1\leq x_i \leq9 1xi9
  3. 相邻的元素差值不超过1。

容易想到dp来求解,定义 d p [ i ] [ j ] 为前 i 个数,且最后一位为 j 的方案数 dp[i][j]为前i个数,且最后一位为j的方案数 dp[i][j]为前i个数,且最后一位为j的方案数

状态转移就比较好写了。

本题还可以使用滚动数组优化空间。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=1e6+10,mod=998244353;
int dp[N][10];

void work()
{
	int n;
	cin>>n;
	for(int i=1;i<=9;i++){
		dp[1][i]=1;
	}
	for(int i=2;i<=n;i++){
		for(int j=1;j<=9;j++){
			if(j==1) dp[i][j]=(dp[i][j]+dp[i-1][j+1]+dp[i-1][j])%mod;
			else if(j==9) dp[i][j]=(dp[i][j]+dp[i-1][j-1]+dp[i-1][j])%mod;
			else dp[i][j]=(dp[i][j]+dp[i-1][j-1]+dp[i-1][j+1]+dp[i-1][j])%mod;
		}
	}
	int ans=0;
	for(int i=1;i<=9;i++) ans=(ans+dp[n][i])%mod;
	cout<<ans<<endl;

}
signed main()
{
	int t;
	t=1;
	while(t--) work();
	return 0;
}

D

题意:多组询问,给定初始字符串 S S S, S S S A , B , C A,B,C A,B,C三种字符组成,要进行 T T T次变化,问变化后第 K K K个字符是什么。

变化规则为: A − > B C , B − > C A , C − > A B A->BC, B->CA, C->AB A>BC,B>CA,C>AB,

0 ≤ T ≤ 1 0 18 0\leq T \leq 10^{18} 0T1018, 1 ≤ K ≤ m i n ( 1 0 18 , S 的长度 ) 1\leq K \leq min(10^{18},S的长度) 1Kmin(1018,S的长度)

很明显模拟每次变化是必然会炸的,那我们看看变化有什么规律,可以发现,每个位置其实都是循环变化的,也就是从 A − > B − > C − > A A->B->C->A A>B>C>A, 对于 S T i S^{T_i} STi的第 K i K_i Ki位,其是由 S T i − 1 S^{T_{i-1}} STi1的第 ( K i + 1 ) / 2 (K_i+1)/2 (Ki+1)/2变化得来的。如果 K i K_i Ki 为偶数,就需要变两次(因为对 A − > B C A->BC A>BC来说,C对应偶数位置, C C C相当于 ( A + 2 ) (A+2) (A+2)。)如果 K i K_i Ki 为奇数,则需变一次。因此可以使用递归求解。

考虑递归的边界:

  1. T = 0 T=0 T=0 时,则答案即为 S K S_{K} SK +相对应的偏移量。
  2. K = 1 K=1 K=1 时,则说明是由第一个字符一直变化得到的,加上偏移量+当前的 T T T(即还需变化的次数)即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=1e5+10,mod=998244353;
int a[N];
int cnt;

void work()
{
	string s;
	cin>>s;
	s=" "+s;
	int q;
	cin>>q;
	while(q--)
	{
		int t,k;
		cin>>t>>k;
		int det=0; // 表示原字符串变化了多少次
		while(t&&k!=1){
			t--;
			if(k%2==0) det+=2;
			else det+=1;
			k=(k+1)/2;
		}
		char ans;
		if(t) { //t很大,这时说明一直在循环,
			ans=(s[1]-'A'+det+t)%3+'A';
		}
		else { //
			ans=(s[k]-'A'+det)%3+'A';
		}
		cout<<ans<<endl;
		
	}
}
signed main()
{
	int t;
	t=1;
	while(t--) work();
	return 0;
}

E

题意:给定字符串S,问有多少字符串满足:

  1. 字典序小于S
  2. 长度等于S
  3. 是回文串

因为要满足是回文串,因此不妨只考虑前 ( n + 1 ) / 2 (n+1)/2 (n+1)/2个字符,如果T的前 ( n + 1 ) / 2 (n+1)/2 (n+1)/2个字符的字典序小于S,则其字典序小于S.因此问题转化为了对于S,有多少个字符串(长度为 ( n + 1 ) / 2 (n+1)/2 (n+1)/2)满足字典序小于S的前 ( n + 1 ) / 2 (n+1)/2 (n+1)/2个字符。

那有多少个字符串满足字典序小于 A B C ABC ABC呢?其实就类似于26进制下,小于 A B C ABC ABC的数有多少个。同时要注意对最后一种情况判断,即T的前面的字典序=S, 假设 S = A B C A A S=ABCAA S=ABCAA, 即我们要判断 A B C B A ABCBA ABCBA 是否小于 S S S 的字典序,如果满足,则答案++ ,

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define x first
#define y second
#define int long long
typedef pair<int,int> pii;
//head
const int mod=998244353;
void work()
{
	int n;
	string s;
	cin>>n>>s;
	s=" "+s;
	int x=(n+1)/2;
	int ans=0;
	//cout<<x<<endl;
	for(int i=1;i<=x;i++){
		ans=ans*26%mod;
		ans=(ans+s[i]-'A')%mod;
	}
	int f=1;
	string s1,s2;
	s1=s2="";
	for(int i=x;i>=1;i--){
		s1+=s[i];
	}
	if(n%2==0) x++;
	for(int i=x;i<=n;i++){
		s2+=s[i];
	}
	if(s1>s2) f=0;
	
	ans=(ans+f)%mod;
	cout<<ans<<endl;
}
signed main()
{
	int t;
	//t=1;
	cin>>t;
	while(t--) work();

	return 0;
}

F

待补。

G

题意:给定序列A, A i A_i Ai 表示 i i i 位置的颜色,询问Q次,每次给出 l , r l,r l,r, 问位置 l 到 r l到r lr最多有对位置颜色相同。

应该算是个莫队模板题。借此来复习一下莫队,莫队本质上是优雅的暴力,其是把在线询问转化为离线询问(注意其不支持在线修改),把所有询问存储下来,排序,然后暴力的更新即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=1e6+10;

int a[N],cnt[N],block[N];
int ans[N];
int n,m;
struct node{
	int l,r,id;
	bool operator <(const node &t) const{
		if(block[l]!=block[t.l]) return l<t.l;
		if(block[l]&1) return r<t.r;//奇偶优化
		return r>t.r;
	}
}qu[N];
int sum;
void modify(int x,int c)
{
	cnt[a[x]]+=c;
	if(c==1&&cnt[a[x]]%2==0) sum++;
	else if(c==-1&&cnt[a[x]]&1) sum--;
}
void work()
{
	cin>>n;
	int b=sqrt(n);
	for(int i=1;i<=n;i++) block[i]=i/b;//块的大小

	for(int i=1;i<=n;i++) cin>>a[i];
	cin>>m;
	for(int i=1;i<=m;i++){
		int l,r;
		cin>>l>>r;
		qu[i]={l,r,i};
	}
	sort(qu+1,qu+1+m);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		//扩大区间是先加减,因为要更新的是下一个状态
		//缩小区间是后加减,因为要更新的是当前状态
		while(l<qu[i].l) modify(l++,-1);
		while(l>qu[i].l) modify(--l,1);
		while(r<qu[i].r) modify(++r,1);
		while(r>qu[i].r) modify(r--,-1);
		ans[qu[i].id]=sum;
	}
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<endl;
	}
}
signed main(){
	ios;
	int t;
	//cin>>t;
	t=1;
	while(t--) work();

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值