恩歐挨批模擬試題-50


水博客太快樂了

RT

有好幾場模擬賽的博客沒有寫,原因是前段時間認為寫博客貌似沒有什么用,不如用寫博客的時間去看看一些奇奇怪怪的題和其他知識點。
然而事實證明水博客還是很有必要的。

考場

發現 \(T1\) 貌似很好寫,和一道叫彈飛綿羊的題很像,只不過被搬到了樹上,然而彈飛綿羊是一道分塊的題。。。
被樹剖困惑了很久,結果發現其實只要倍增一下即可。。。
然而浪費了兩個多小時。

之后剩下一個多小時,感覺可以 \(T2 \ T3\) 還有好多部分分,於是選擇先寫 \(T2\) ,本來想先寫暴力,再想正解,然而樣例鍋了。。。多了一個換行,寫了好幾種暴力(其中包含多正解起有啟發的方法),發現輸出的都是相同的錯誤答案,才發現問題,結果寫完暴力只剩十分多鍾了,沒時間搞 \(T3\) 了,只能坐等出分。

分數

預估 : \(t1 \ 100pts \ + \ t2 \ 20pts \ + \ t3 \ 0pts \ = \ 120pts\)
實際 : \(t1 \ 100pts \ + \ t2 \ 20pts \ + \ t3 \ 0pts \ = \ 120pts\)

題解

A. 第零題

首先,對於每個點,很容易求出從它開始向上走(顯然一個棧就可以了,對吧!? ^_^),走到哪里會死,為了快速查詢,不難想到講這個數組倍增一下,即 \(p_{u, i}\) 表示從點 \(u\) 向上走,死 \(2^i\) 次后的位置。

對於每次詢問 \(s, t\),首先找到這兩個點的 \(lca\) ,然后倍增求出從 \(s\) 走到 \(lca\) 會死多少次。但是從 \(s\) 死到 \(lca\) 不一定剛好會在 \(lca\) 處死亡,而是在鏈 \((s, lca)\)\(lca\) 下方的某一個點 \(u\) 處,接着要從 \(lca\) 走向 \(t\) ,不妨先在鏈 \((lca, t)\) 上先找到一個點 \(v\) ,使得從 \(u\) 走到 \(lca\) 再走到 \(v\) 剛好死亡(顯然也可以倍增來求),接着只要從 \(v\) 走到 \(t\) 即可。
從上往下走很麻煩,此時有一個性質,從 \(u\) 走到 \(v\) 的死亡次數與從 \(v\) 走到 \(u\) 的死亡次數相同,這個很好證明(相信從明的您一定會的,對吧!? ^_^),隨便畫一畫就出來了。。。這樣就把從上往下走轉變成了從下往上走。。。
大概就這樣?也沒什么細節要注意的。。。

時間復雜度 : \(O(n\log n)\)

code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define t first
#define w second
const int N=2e5+10;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, k, m;
vector<pair<int, int> > l[N];
int fa[N][21], plc[N][21], dep[N], stk[N], top, l1;
ll dis[N];
void dfs1(int u, int f1, int dis1){
	dep[u]=dep[f1]+1; dis[u]=dis[f1]+dis1; fa[u][0]=f1; stk[++top]=u;
	int ul=l1;
	while(dis[u]-dis[stk[l1+1]]>=k&&l1<top) ++l1;
	if(dis[u]-dis[stk[l1]]>=k) plc[u][0]=stk[l1];
	for(pair<int, int> v : l[u])
		if(v.t!=f1) dfs1(v.t, u, v.w);
	l1=ul; --top;
}
void dfs2(int u){
	for(int i=1; i<20; ++i){
		plc[u][i]=plc[plc[u][i-1]][i-1];
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(pair<int, int> v : l[u])
		if(v.t!=fa[u][0]) dfs2(v.t);
}
inline int lca(int u, int v){
	if(dep[u]<dep[v]) u^=v, v^=u, u^=v;
	for(int i=19; ~i; --i)
		if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
	if(u==v) return u;
	for(int i=19; ~i; --i)
		if(fa[u][i]!=fa[v][i]) u=fa[u][i], v=fa[v][i];
	return fa[u][0];
}
inline int solve(int u, int v){
	int Lca=lca(u, v), ans=0, ul, v1=v;
	for(int i=19; ~i; --i)
		if(dep[plc[u][i]]>=dep[Lca]) ans+=1<<i, u=plc[u][i];
	ul=k-(dis[u]-dis[Lca]);
	for(int i=19; ~i; --i)
		if(dis[fa[v1][i]]-dis[Lca]>=ul) v1=fa[v1][i];
	if(dis[v1]-dis[Lca]>=ul) ++ans;
	for(int i=19; ~i; --i)
		if(dep[plc[v][i]]>=dep[v1]) ans+=1<<i, v=plc[v][i];
	return ans;
}
int main(void){
	n=read(), k=read(); int x, y, z;
	for(int i=1; i<n; ++i){
		x=read(), y=read(), z=read();
		l[x].push_back(make_pair(y, z));
		l[y].push_back(make_pair(x, z));
	}
	dfs1(1, 0, 0); dfs2(1);
	m=read();
	while(m--){
		x=read(), y=read();
		printf("%d\n", solve(x, y));
	}
	return 0;
}

B. 第負一題

一道陰間友善的分治題。。。

對於分治區間,求出跨過當前區間中點的答案和,對於每左區間的點 \(i\) ,不難求出 \(l_{i,0}\ l_{i,1}\) 分別表示 \([i,mid]\) ,選不選 \(mid\) 的答案。同理,對於每個友區間的點 \(j\) ,也不難求出 \(r_{i,0}\ r_{i,1}\) ,分別表示 \([mid+1,r]\) ,選不選 \(mid+1\) 的答案。
若要合並一個區間,對於 \([i,j]\) ,則答案顯然是 \(max(l_{i,0}+r_{j,0}, l_{i,1}+r_{j,0}, l_{i,0}+r_{j,1})\)
不妨設 \(L_{i}=max(0, l_{i,1}-l_{i,0}),\ R_{i}=max(0, r_{i,1}-r_{i,0})\)
則答案可以表示成 : \(l_{i,0}+r_{j,0}+max(L_{i},R_{j})\)
發現對於區間 \([i,j]\) \(l_{i,0}\)\(r_{j,0}\) 一定會被記錄到答案中,而 \(L_{i}\) 若對區間 \([i,j]\) 有貢獻,當且僅當 \(L_i > R_j\)\(R_j\) 同理,因此將 \(L\)\(R\) 放在一起排序后統計即可。

code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fi first
#define se second
const ll N=2e5+10, INF=0x3f3f3f3f3f3f3f3f, mod=998244353;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, top, a[N];
ll ans, f[N][2][2];
pair<ll, int > stk[N];
void solve(int l, int r){
	if(l==r) { (ans+=a[l])%=mod; return; }
	int mid=(l+r)>>1, numl=0, numr=0;
	solve(l, mid); solve(mid+1, r); top=0;
	f[mid+1][1][1]=a[mid+1]; f[mid+1][0][0]=0;
	f[mid+1][1][0]=f[mid+1][0][1]=-INF;
	for(int i=mid+2; i<=r; ++i){
		f[i][1][0]=max(f[i-1][1][0], f[i-1][1][1]);	f[i][1][1]=f[i-1][1][0]+a[i];
		f[i][0][0]=max(f[i-1][0][0], f[i-1][0][1]);	f[i][0][1]=f[i-1][0][0]+a[i];
		(ans+=max(f[i][0][1], f[i][0][0])*(mid-l+1)%mod)%=mod;
	}
	f[mid][1][1]=a[mid]; f[mid][0][0]=0;
	f[mid][1][0]=f[mid][0][1]=-INF;
	for(int i=mid-1; i>=l; --i){
		f[i][1][0]=max(f[i+1][1][0], f[i+1][1][1]); f[i][1][1]=f[i+1][1][0]+a[i];
		f[i][0][0]=max(f[i+1][0][0], f[i+1][0][1]); f[i][0][1]=f[i+1][0][0]+a[i];
		(ans+=max(f[i][0][1], f[i][0][0])*(r-mid)%mod)%=mod;
	}
	for(int i=l; i<=r; ++i)
		stk[++top]=make_pair(max(max(f[i][1][1], f[i][1][0])-max(f[i][0][1], f[i][0][0]), 0ll), i);
	sort(stk+1, stk+1+top);
	for(int i=1; i<=top; ++i){
		if(stk[i].se<=mid){
			(ans+=stk[i].fi*numr%mod)%=mod;
			++numl;
		}else{
			(ans+=stk[i].fi*numl%mod)%=mod;
			++numr;
		}
	}
}
int main(void){
	n=read();
	for(int i=1; i<=n; ++i) a[i]=read();
	solve(1, n); printf("%lld\n", ans);
	return 0;
}

C. 第負二題

毒瘤出的陰間題憑什么讓我一個陽間人來做???

居然有四個人場切。。。
然而后來數據被加強了。。。

首先對於 \(i\) 來說,考慮一個答案 \(k\) 成立需滿足那些條件。
不難發現需要存在一個中心在 \(i\) 這一行的菱形滿足其對角線長度為 \(2k-1\)
設中心為 \(mid\) ,則需滿足 :
\(\forall j \in [i-k+1,i+k-1], \ mid+(k-|j-i|-1) \le r_i \& mid-(k-|j-i|-1) \ge l_i\)
也就是 :
\(min(r_j-(k-|j-i|-1)) \ge max(l_j+(k-|j-i|-1))\)
由於數據被加強了,卡掉了 \(RMQ\)\(\log\)的做法(不知道四毛子能不能過??),只能用單調隊列維護最大值最小值(所以需要四個單調隊列。。。)。
不難發現 \(ans_i \in [ans_{i-1}-1, ans_{i-1}+1]\),於是直接枚舉即可。

一堆細節。。。麻煩。。。

code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e6+10, mod=998244353, INF=0x3f3f3f3f;
typedef unsigned long long u64;
inline u64 read(){
	u64 f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
   
u64 xorshift128p(u64 &A, u64 &B) { 
    u64 T = A, S = B; 
    A = S; 
    T ^= T << 23; 
    T ^= T >> 17; 
    T ^= S ^ (S >> 26); 
    B = T; 
    return T + S; 
} 

void gen(int n, int L, int X, int Y, u64 A, u64 B, int l[], int r[]) { 
    for (int i = 1; i <= n; i ++) { 
        l[i] = xorshift128p(A, B) % L + X; 
        r[i] = xorshift128p(A, B) % L + Y; 
        if (l[i] > r[i]) swap(l[i], r[i]); 
    } 
}
int n;
u64 A, B, X, Y, L;
int l[N], r[N];
int q1[N], q2[N], q3[N], q4[N], l1, r1, l2, r2, l3, r3, l4, r4, f[N];
ll ans;
inline void add(int p){
	while(l1<=r1&&l[q1[r1]]-q1[r1]<=l[p]-p) --r1;
	while(l2<=r2&&r[q2[r2]]+q2[r2]>=r[p]+p) --r2;
	q1[++r1]=p, q2[++r2]=p;
}
inline void add1(int p){
	while(l3<=r3&&l[q3[r3]]+q3[r3]<=l[p]+p) --r3;
	while(l4<=r4&&r[q4[r4]]-q4[r4]>=r[p]-p) --r4;
	q3[++r3]=p, q4[++r4]=p;
}
inline bool chk(int i, int k, int op){
	int L1=-INF, R1=INF;
	if(l1<=r1) L1=l[q1[l1]]-q1[l1]+k+i-1;
	if(l2<=r2) R1=r[q2[l2]]+q2[l2]-k-i+1;
	if(l3<=r3) L1=max(L1, l[q3[l3]]+q3[l3]+k-i-1);
	if(l4<=r4) R1=min(R1, r[q4[l4]]-q4[l4]-k+i+1);
	for(int j=1; j<=op; ++j){
		L1=max(L1, l[i+k-j]-(i+k-j)+i+k-1);
		R1=min(R1, r[i+k-j]+(i+k-j)-i-k+1);
	}
	return L1<=R1;
}
signed main(void){
	freopen("sample.in", "r", stdin);
	freopen("wrong.out", "w", stdout);
	n=read(); L=read(), X=read(), Y=read(), A=read(), B=read();
	gen(n, L, X, Y, A, B, l, r);
	l1=l2=l3=l4=1; f[1]=1;
	for(int i=2; i<=n; ++i){
		while(q1[l1]<i&&l1<=r1) ++l1;
		while(q2[l2]<i&&l2<=r2) ++l2;
		f[i]=f[i-1]+1; add1(i-1);
		for(int j=2; j; --j){
			if(chk(i, f[i], j)){
				for(int k=j; k; --k) add(i+f[i]-k);
				break;
			}else{
				--f[i];
				while(l3<=r3&&q3[l3]<=i-f[i]) ++l3;
				while(l4<=r4&&q4[l4]<=i-f[i]) ++l4;
			}
		}
	}
	ll mul=1;
	for(int i=1; i<=n; ++i){
		(ans+=1ll*mul*f[i]%mod)%=mod;
		(mul*=3)%=mod;
	}
	printf("%lld\n", ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM