test20231030
rp 大爆发(别一次用完就行了)。
来晚了,差点没赶上考试。
T1
先看 T1,看上去很像一个三维偏序问题,一看数据范围 \(n\le 3\times 10^7\)。
不行,再看一眼题目,发现一句话 请选手仔细观察给出的数据生成器,数据生成方式与解题强相关
。
阿这,原来是一道分析代码题。
看他数据生成器:
typedef unsigned long long ull;
int n,A,B,C,u[N],v[N],w[N];
inline ull Rand(ull &k1, ull &k2){
ull k3 = k1, k4 = k2;
k1 = k4;
k3 ^= (k3 << 23);
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
inline void GetData () {
ull x, y;
cin >> n >> A >> B >> C >> x >> y;
up(i,1,n){
u[i] = Rand(x, y) % A + 1;
v[i] = Rand(x, y) % B + 1;
w[i] = Rand(x, y) % C + 1;
if (Rand(x, y) % 3 == 0) u[i] = A;
if (Rand(x, y) % 3 == 0) v[i] = B;
if ((u[i] != A) && (v[i] != B)) w[i] = C;
}
}
我们发现 \(u_i\),\(v_i\) 有三分之一的概率等于 \(A\),\(B\),\(w_i\) 有九分之四的概率等于 \(C\)。
而且 \(w_i\) 等于 \(C\) 时,\(u_i\),\(v_i\) 分别不等于 \(A\),\(B\)(大概率)。
然后我灵机一动,将操作分成两部分,第一种是 \(u_i\),\(v_i\) 分别等于 \(A\),\(B\),这个时候就只用找到\(\max w_i\) 就行了,另一部分是 \(w_i\) 等于 \(C\) 的,然后把 \(u_i\),\(w_i\) 撒在二维平面,一个点代表一个矩形,求矩形面积就行了,我开始还用单调栈,写一半发现因为矩形的高度具有单调性,所以只用记录后缀最大值扫一遍就行了。
但是这有一个问题,就是有一些点没有被统计,所以我们可以 把所有的 \(\max<w_i\le C\) 都进行一次如下操作~ ,桥豆麻袋,这样复杂度不就 \(n^2\) 了吗?
哎呀~,观察最大的样例,我们发现这个 \(\max \approx C\),而且数据纯随机,自信。
复杂度 \(O(玄学)\)。
signed main(){
GetData();
int maxl=0;
up(i,1,n){
if(u[i]==A&&v[i]==B){
maxl=max(maxl,w[i]);
if(w[i]==C){
i128 t=(i128)A*B*C;
write(t);
return 0;
}
}
}
ans+=(i128)A*B*maxl;
i128 sq=0,pre=0;
dn(H,C,maxl+1){
up(i,1,n){
if(w[i]==H){
suf[u[i]]=max(suf[u[i]],v[i]);
}
}
sq=0;
for(int i=A;i>=1;i--){
suf[i]=max(suf[i],suf[i+1]);
sq+=suf[i];
}
ans+=(i128)(H-maxl)*(sq-pre);
pre=sq;
}
write(ans);
//cout<<ans<<endl;
return 0;
}
T2
出去洗了把脸看 T2,是期望不是很会的样子。
与前两天的 At_abc_E 类似,
把最后要求的东西看成 \(\sum_{i=1}^{n} i*p_i\)。
所以我们只用把每一个数留到最后的概率求出来。
然后快速写了个状压求概率。
然后考虑进一步优化。
这个 DP 可不可以用更简单的形式表达出来呢?
对于每一个数,我们似乎只用关心他前面有多少个数就行了,最后的状态就是他排名为 \(1\),的情况,想到了这个,这道题就基本想出来了,接下来就只有代码实现问题(然后我在实现上卡了一个小时)。
int n,p[N];
int ans=0;
signed main(){
n=read();
up(i,1,n-1){
p[i]=read();
}
up(w,1,n){
memset(dp,0,sizeof dp);
dp[1][w]=1;
up(i,2,n){
up(j,1,n){
if(j<=n-i+1)dp[i][j]=(dp[i][j]+dp[i-1][j]*ksm((1-p[i-1]+mod)%mod,j))%mod;
if(j!=1)dp[i][j-1]=(dp[i][j-1]+dp[i-1][j]*(1-ksm((1-p[i-1]+mod),j-1)+mod)%mod)%mod;
}
}
ans=(ans+w*dp[n][1])%mod;
}
cout<<ans;
return 0;
}
T3
看着不是很会,于是写了 \(15\),分的 \(n^3\) 暴力加上输出 \(0\) 骗分。
luogu 竟然能的 \(35\) 分。
看着题目,一点头绪都没有。
忽然,在数据点的最后一句话 \(b_i\ge b_{i+1}\)。
tnnd,这么重要的性质你给放在最后一行!!
既然 \(b\) 数组具有单调性,如果一个数字 \(p\),导致一个区间不可行,那么这个区间包括这个数字的其他子区间必定不可行。
考虑分治,令 \(f(l,r)\) 表示更新到 \([l,r]\) 这个区间,把所有个数少于 \(b_{r-l+1}\) 的数字删掉,那么这个区间就会分成多个子区间,然后再分别进行递归。如果有区间不被删掉,那么直接统计进入答案。
但是这有可能会让时间复杂度退化到 \(n^2\)。
所以我们可以树上启发式合并优化。
int n;
int a[N],b[N],cnt[N];
int ans;
void solve(int l, int r) {
if (r<l||r-l+1<=ans) {
up(i,l,r)--cnt[a[i]];
return;
}
if(l==r){
--cnt[a[l]];
if(b[1]==1)ans=max(ans,1);
return;
}
int p=l,q=r,t=-1,val=b[r-l+1];
while(p<=q){
if(cnt[a[p]]<val){t=p;break;}
if(cnt[a[q]]<val){t=q;break;}
++p,--q;
}
if(t<0){
ans=max(ans,r-l+1);
up(i,l,r)--cnt[a[i]];
return;
}
int mid=(l+r)>>1;
if(t<=mid){
up(i,l,t)--cnt[a[i]];
solve(t+1,r);
up(i,l,t-1)++cnt[a[i]];
solve(l,t-1);
}
else{
up(i,t,r)--cnt[a[i]];
solve(l,t-1);
up(i,t+1,r)++cnt[a[i]];
solve(t+1,r);
}
}
signed main() {
n=read();
up(i,1,n)a[i]=read();
up(i,1,n)b[i]=read();
up(i,1,n)cnt[a[i]]++;
solve(1,n);
cout<<ans;
return 0;
}
T4
题都没看。
给你一个序列,区间查询极长相等子段长度的平方和的期望,外加单点修改,如果数列中一个数是 \(0\),表示这个数是 \([1,m]\) 中均匀随机。答案 \(\mod 998244353\)。
地狱,太地狱了。
首先进行一步转化,极长连续段的平方等于满足 \(x_i=x_{i+1}...=x{j}\) 的数对 \((i,j)\) 的数量。期望即为 \(x_i=x_{i+1}...=x{j}\) 的概率。
- 如果 \((i,j)\) 中有多种颜色,那么 \(p_{i,j}=0\)。
- 如果 \((i,j)\) 中有 \(1\) 种颜色,那么 \(p_{i,j}=m^{-t}\)。
- 如果 \((i,j)\) 中一种颜色都没有,那么 \(p_{i,j}=m^{-t+1}\)。
\(ans=\sum_{i=1}^{n}\sum_{j=i+1}^{n} p_{i,j}\times 2+n\)
可以用线段树维护。
只需要维护:该区间内概率的和,左边第一个确定的颜色,右边第一个确定的颜色,区间长度,区间中未确定的数的数量,$[L,i]/[i,R] \(中有且仅有一种确定的颜色时/没有确定的颜色时\)[m^{-t}]$中未确定的数的数量 的和。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10, mod = 998244353;
int Pow (int a, int k) {
int res = 1;
for (; k; k >>= 1, a = 1ll * a * a % mod)
if (k & 1) res = 1ll * res * a % mod;
return res;
}
int n, c, q, a[N], T[3][N], f[N], inv;
struct Seg {
int l, r, lc, rc, lans, rans, tans, all, tag;
} seg[N << 3], ans;
bool flag;
Seg Merge (Seg a, Seg b) {
Seg res;
res.l = (!a.lc) ? a.l + b.l : a.l;
res.r = (!b.rc) ? a.r + b.r : b.r;
res.lc = (!a.lc) ? b.lc : a.lc;
res.rc = (!b.rc) ? a.rc : b.rc;
(a.lc) ? res.lans = a.lans : res.lans = 0;
if (a.tag) {
if (a.lc) res.lans = (res.lans + (T[1][a.all + b.l] - T[1][a.all] + mod) % mod) % mod;
if (a.lc && b.lc && a.lc != b.lc);
else res.lans = (res.lans + 1ll * T[0][a.all] * b.lans % mod) % mod;
}
(b.rc) ? res.rans = b.rans : res.rans = 0;
if (b.tag) {
if (b.rc) res.rans = (res.rans + (T[1][b.all + a.r] - T[1][b.all] + mod) % mod) % mod;
if (a.rc && b.rc && a.rc != b.rc);
else res.rans = (res.rans + 1ll * T[0][b.all] * a.rans % mod) % mod;
}
res.tans = (a.tans + b.tans) % mod;
int x = T[1][a.r], y = T[1][b.l];
res.tans = (res.tans + 1ll * T[2][a.r] * y % mod) % mod;
if (!a.rc || !b.lc || a.rc == b.lc)
res.tans = (res.tans + (1ll * ((a.rans + x) % mod) * ((b.lans + y) % mod) % mod - 1ll * x * y % mod + mod) % mod) % mod;
else res.tans = (res.tans + 1ll * x * b.lans % mod) % mod, res.tans = (res.tans + 1ll * y * a.rans % mod) % mod;
res.tag = a.tag & b.tag & (!a.lc || !b.lc || a.lc == b.lc);
res.all = a.all + b.all;
return res;
}
void Build (int p, int l, int r) {
if (l == r) {
if (a[l]) seg[p] = (Seg){0, 0, a[l], a[l], 1, 1, 1, 0, 1};
else seg[p] = (Seg){1, 1, 0, 0, 0, 0, 1, 1, 1};
return;
}
Build (p << 1, l, (l + r) >> 1);
Build (p << 1 | 1, ((l + r) >> 1) + 1, r);
seg[p] = Merge(seg[p << 1], seg[p << 1 | 1]);
}
void Update (int p, int l, int r, int k) {
if (l == r) {
if (a[l]) seg[p] = (Seg){0, 0, a[l], a[l], 1, 1, 1, 0, 1};
else seg[p] = (Seg){1, 1, 0, 0, 0, 0, 1, 1, 1};
return;
}
if((l + r) >> 1 >= k) Update(p << 1, l, (l + r) >> 1, k);
else Update(p << 1 | 1, ((l + r) >> 1) + 1, r, k);
seg[p] = Merge(seg[p << 1], seg[p << 1 | 1]);
}
void Query (int p, int l, int r, int kl, int kr) {
if (kl > r || kr < l) return;
if (kl <= l && kr >= r) {
if (!flag) flag = 1, ans = seg[p];
else ans = Merge(ans, seg[p]);
return;
}
Query (p << 1, l, (l + r) >> 1, kl, kr);
Query (p << 1 | 1, ((l + r) >> 1) + 1, r, kl, kr);
}
int main() {
scanf("%d%d%d", &n, &c, &q);
inv = Pow(c, mod - 2);
for (int i = 0; i < 2; i++) {
T[i][0] = 1;
int x = i ? Pow(c, mod - 2) : c;
for (int j = 1; j <= n; j++) T[i][j] = 1ll * T[i][j - 1] * x % mod;
T[i][0] = 0;
for (int j = 2; j <= n; j++) T[i][j] = (T[i][j - 1] + T[i][j]) % mod;
}
T[0][0] = 1;
for (int j = 1; j <= n; j++) T[0][j] = 1ll * T[0][j - 1] * inv % mod;
for (int j = 0; j <= n; j++) T[2][j] = 1ll * T[1][j] * c % mod;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
Build(1, 1, n);
for (int i = 1, op, tx, ty; i <= q; i++) {
scanf("%d%d%d", &op, &tx, &ty);
if (op == 1) a[tx] = ty, Update(1, 1, n, tx);
else flag = 0, Query(1, 1, n, tx, ty), printf("%d\n", (1ll * ans.tans * 2 % mod - (ty - tx + 1) + mod) % mod);
}
return 0;
}