ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

恩欧挨批模拟式题-50

2021-09-11 12:00:38  阅读:153  来源: 互联网

标签:ch int ans 50 mid fa lca 模拟式 恩欧


水博客太快乐了

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

标签:ch,int,ans,50,mid,fa,lca,模拟式,恩欧
来源: https://www.cnblogs.com/CTcode/p/NOIP_50.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有