test20231030

rp 大爆发(别一次用完就行了)。

来晚了,差点没赶上考试。

T1

先看 T1,看上去很像一个三维偏序问题,一看数据范围 \(n\le 3\times 10^7\)

image

不行,再看一眼题目,发现一句话 请选手仔细观察给出的数据生成器,数据生成方式与解题强相关

阿这,原来是一道分析代码题。

看他数据生成器:

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;
}

posted @ 2023-10-30 15:22  LiQXing  阅读(31)  评论(0)    收藏  举报