jls 第一场 真的不会场……
补题进度 【3/10】;场上 2
A Rikka with Lowbit
这道题的意思就是给一个数进行加它的lowbit或者减lowbit操作,这两个操作各有1/2的概率,问一个区间的区间和的期望。
以为有什么公式,结果手算了样例,发现样例中每次查询的值不变是真的。
所以就变成普通区间求和了。因为概率均等,加减的值都一样,所以加在一起就没了……
#include<cstring>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
long long pow_mod(long long a,long long n,long long m)//a^n mod m
{
long long res=1;
while(n>0)
{
if(n&1==1)
res=res*a%m;
a=a*a%m;
n>>=1;
}
return res;
}
//记得在最后输出结果的时候再模m一次
int a[100010];
ll sum[100010];
int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
int opt,l,r;
ll n,m;
while(T--)
{
scanf("%lld%lld",&n,&m);
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&opt,&l,&r);
if(opt==1)continue;
ll ans=(sum[r]-sum[l-1])%mod*pow_mod(2,n*m,mod);
ans%=mod;
printf("%lld\n",ans);
}
}
return 0;
}
J Rikka with Nickname
问的是将字符串顺次拼接,在前面的串不变,在后面的串只要是新串的子序列就行,求最小字典序的拼接结果。
一开始以为是什么后缀数组,结果仔细看了眼数据范围,就写了个暴力……
其实只要每次保存一下每个字符出现的位置,然后对于新串的从前往后的字符,找到一个存在的位置,找不到就把剩下的字符添加到最后面。
查找的过程可以二分一下。
#include <bits/stdc++.h>
#include <cstring>
using namespace std;
const int MAXN = 1e6 + 5;
vector<int> pos[30];
char str[MAXN],tmp[MAXN];
typedef vector<int>::iterator vit;
int main()
{
int ca;
scanf("%d",&ca);
while(ca--)
{
int n;
memset(str,0,sizeof(str));
for(int i = 0; i<30; i++)
pos[i].clear();
scanf("%d",&n);
scanf("%s",str);
int tot = strlen(str);
for(int i = 0; i<tot; i++)
{
pos[str[i] - 'a'].push_back(i);
}
for(int j = 0; j<n-1; j++)
{
scanf("%s",tmp);
int len = strlen(tmp);
int pre = -1;
int i;
bool flag = 1;
for(i = 0; i<len; i++)
{
int id = tmp[i] - 'a';
if(!pos[id].empty())
{
//cout<<pre<<' ';
if(pre == -1)
pre = pos[id][0];
else
{
vit cur = upper_bound(pos[id].begin(),pos[id].end(),pre);
//cout<<*cur<<' ';
if(cur != pos[id].end())
{
pre = *cur;
}
else
{
flag = 0;
break;
}
}
//cout<<pre<<endl;
}
else
{
flag = 0;
break;
}
}
if(!flag)
{
for(int j = i; j<len; j++)
{
str[tot] = tmp[j];
pos[tmp[j]-'a'].push_back(tot++);
}
//cout<<str<<endl;
}
}
printf("%s\n",str);
}
return 0;
}
ps:我居然再通知改数据的一瞬间A了,但是之前交的都没过。感觉是C++哪里不兼容
D Rikka with Prefix Sum
题意很简单,就是没有搞出来。
这个其实将前缀和转化成组合数计算,然后根据每次求和操作建立时间戳。
这道题和9的一道题有点类似,由于没有出那道题,这道题也没想明白。
看样子需要写个单独的文章学习一下。
#include<bits/stdc++.h>
#define ff first
#define ss second
#define pb push_back
#define mp make_pair
#define ll long long
#define all(a) a.begin(),a.end()
#define endl '\n'
using namespace std;
const int maxn = 2e5 + 1;
const ll mod = 998244353;
ll inv[maxn],fac[maxn];
void init()
{
inv[0]=fac[0]=1;
inv[1]=1;
for(int i=1; i<maxn; i++)
fac[i]=fac[i-1]*i%mod;
inv[1]=1;
for(int i=2; i<maxn; i++)
inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
inv[0]=1;
for(int i=1; i<maxn; i++)
inv[i]=inv[i-1]*inv[i]%mod;
}
ll com(int n,int m)
{
if(m > n || n< 0 || m < 0) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll pow_mod(ll a,ll n)
{
ll res = 1,b = a % mod;
while(n)
{
if(n & 1)
res = res * b % mod;
b = b *b % mod;
n >>= 1;
}
return res;
}
struct Query
{
int l,d,v;
};
vector<Query> Q;
int main()
{
init();
int ca;
scanf("%d",&ca);
while(ca--)
{
int n,m;
scanf("%d%d",&n,&m);
int op,l,r,v;
int cnt= 0;
Q.clear();
for(int i = 0; i<m; i++)
{
scanf("%d",&op);
if(op == 1)
{
scanf("%d%d%d",&l,&r,&v);
Q.pb({l,cnt-1,v});
Q.pb({r+1,cnt-1,-v});
}
else if(op == 2)
cnt++;
else
{
scanf("%d%d",&l,&r);
ll ans = 0,tmp;
for(auto &q : Q)
{
tmp = cnt - q.d;
if(q.l <= l-1)
{
ans = (ans - q.v *com(l - q.l + tmp -1,l - q.l -1) % mod + mod) % mod;
ans = (ans % mod + mod)% mod;
}
if(q.l <= r)
{
ans = ( ans + q.v * com(r - q.l + tmp,r - q.l) % mod + mod) % mod;
ans = (ans % mod + mod) % mod;
}
}
printf("%lld\n",ans % mod);
}
}
}
return 0;
}