AtCoder Beginner Contest 242
题意:求出满足下列条件的序列的方案数。
- 长度为 n n n.
- 对于任意的 i i i, 1 ≤ x i ≤ 9 1\leq x_i \leq9 1≤xi≤9。
- 相邻的元素差值不超过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;
}
题意:多组询问,给定初始字符串 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} 0≤T≤1018, 1 ≤ K ≤ m i n ( 1 0 18 , S 的长度 ) 1\leq K \leq min(10^{18},S的长度) 1≤K≤min(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}} STi−1的第 ( 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 为奇数,则需变一次。因此可以使用递归求解。
考虑递归的边界:
- T = 0 T=0 T=0 时,则答案即为 S K S_{K} SK +相对应的偏移量。
- 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;
}
题意:给定字符串S,问有多少字符串满足:
- 字典序小于S
- 长度等于S
- 是回文串
因为要满足是回文串,因此不妨只考虑前 ( 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;
}
待补。
题意:给定序列A, A i A_i Ai 表示 i i i 位置的颜色,询问Q次,每次给出 l , r l,r l,r, 问位置 l 到 r l到r l到r最多有对位置颜色相同。
应该算是个莫队模板题。借此来复习一下莫队,莫队本质上是优雅的暴力,其是把在线询问转化为离线询问(注意其不支持在线修改),把所有询问存储下来,排序,然后暴力的更新即可。
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;
}