10086

my first title

my first paragraph

根号分治(待补充)

最近有根号分治的题都没那么熟悉,想到了方向感也很差,故写一点题解。

LCA查询

看到题目中的条件 k105

说明 kS 的个数很多,Sk 的个数很少。

那么对于前者,考虑一开始最朴素的 O(S2) 的暴力,枚举集合中的两个点,求 LCA 总时间复杂度 O(NSS2logN)

对于后者,若 n2 肯定不行。考虑到我们的限制:一共也就 n 个点,先让 A 集合里的点向上跳,将点都标起来,若跳到该点已经走过就不用再跑,这样保证了每个点只会被遍历 1 次。再让 B 里的点向上跳,若碰到 A 的标记,进行深度统计,不再向上(应为上面不可能更优)。时间复杂度 O(NSN)

跑起来挺快。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m,dep[N];
int siza,a[N];
int sizb,b[N];
int st[N][18];
vector<int> e[N];
void dfs(int u,int fa){
	dep[u] = dep[fa] + 1;
	st[u][0] = fa;
	for(int i = 1;i<=17;++i)st[u][i] = st[st[u][i-1]][i-1];
	for(int v : e[u])if(v ^ fa)dfs(v,u);
}
int LCA(int x,int y){
	if(dep[x] > dep[y])swap(x,y);
	int t = dep[y] - dep[x];
	for(int i = 17;~i;--i)if(t&(1<<i))y = st[y][i];
	if(x == y)return x;
	for(int i = 17;~i;--i)if(st[x][i] ^ st[y][i])
		x = st[x][i], y = st[y][i];
	return st[x][0];
}
int vis[N];
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1,u,v;i<n;++i){
		scanf("%d%d",&u,&v);
		e[u].push_back(v),e[v].push_back(u);
	}
	dfs(1,0);
	
	while(m--){
		scanf("%d",&siza); for(int i = 1;i<=siza;++i)scanf("%d",&a[i]);
		scanf("%d",&sizb); for(int i = 1;i<=sizb;++i)scanf("%d",&b[i]);
		
		int ans = 0;
		if(siza <= 100 && sizb <= 100){
			for(int i = 1;i<=siza;++i)
				for(int j = 1;j<=sizb;++j)
					ans = max(ans,dep[LCA(a[i],b[j])]);
			printf("%d\n",ans);
		}else{
			for(int i = 1;i<=siza;++i){
				int now = a[i];
				while(now){
					if(vis[now] > 0)break;
					vis[now] = 1;
					now = st[now][0];
				}
			}
			for(int i = 1;i<=sizb;++i){
				int now = b[i];
				while(now){
					if(vis[now] > 0){
						if(vis[now] == 1)ans = max(ans,dep[now]);
						break;
					}
					vis[now] = 2;
					now = st[now][0];
				}
			}
			memset(vis,0,sizeof (int)*(n+5));
			printf("%d\n",ans);
		}
	}
	return 0;
}

CF1997E Level Up

同样考虑二元关系。

可以想到随着 k 的增加,一级一级跳的次数是越来越少的

对于 kB,直接暴力模拟

对于 k>B,考虑式子 [levela[j]]k 二分下一个升级的位置。

同时 sum 可能用主席树维护,但根号分治的二元关系又体现了出来。

由于 k>B,故 levelNB,直接预处理数组即可。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N = 2e5 + 10;
const int B = 440;
int n,q,f[N],a[N];
int sum[450][N];
bool mark[N];
int rk[N];
inline bool check(int l,int r,int level,int k){
	return (r - l + 1) - (sum[level-1][r] - sum[level-1][l-1]) >= k;
}
vector<pii> qry[N];
int ans[N];
int main(){
	scanf("%d%d",&n,&q);
	for(int i = 1;i<=n;++i){
		scanf("%d",&a[i]);
		if(a[i] <= B)++sum[a[i]][i];
	}
	for(int j = 1;j<=B;++j)
		for(int i = 1;i<=n;++i)
			sum[j][i] += sum[j-1][i] + sum[j][i-1] - sum[j-1][i-1];
	for(int i = 1,p,k;i<=q;++i){
		scanf("%d%d",&p,&k);
		qry[k].push_back({p,i});
	}
	for(int k = 1;k<=n;++k){
		if(qry[k].empty())continue;
		if(k <= B){
			int level = 1, cnt = 0;
			for(int i = 1;i<=n;++i){
				mark[i] = 0;
				if(a[i] < level){
					mark[i] = 1;
					continue;
				}
				++cnt;
				if(cnt == k)cnt = 0,++level;
			}
			for(pii p : qry[k])if(!mark[p.first])ans[p.second] = 1;
		}else{
			sort(qry[k].begin(),qry[k].end());
			int pos = 1,level = 1,it = 0;
			while(pos <= n){
				int L = pos, R = n, res = n;
				while(L <= R){
					int mid = L + R >> 1;
					if(check(pos,mid,level,k))res = mid, R = mid - 1;
					else L = mid + 1;
				}
				while(it < qry[k].size() && pos <= qry[k][it].first && qry[k][it].first <= res)
					rk[qry[k][it].first] = level,++it;
				pos = res + 1;
				++level;
			}
			for(pii p : qry[k])ans[p.second] = (a[p.first] >= rk[p.first]);
		}
	}
	for(int i = 1;i<=q;++i)
		puts(ans[i] ? "YES" : "NO");
	return 0;
}

ABC365G

问题陈述

N 人在办公室工作。

该办公室保存着进出记录,自记录开始以来,已有 M 人进出。

i(1iM) 记录由一对整数 (Ti,Pi) 表示,表明在 Ti 时间, Pi 如果在办公室外,则进入了办公室,如果在办公室内,则离开了办公室。

已知所有的人在记录开始时都在办公室外。

按以下格式回答 Q 查询。

对于 i -th (1iQ) 查询,你会得到一对整数 (Ai,Bi) 。求自记录开始以来,AiBi 这两个人同时在办公室内的时间段的总长度。

分析

对于 段数 B 的,暴力扫过来统计

对于 段数 >B 的, 考虑预处理, 这样的 i 最多有 N/B

对于每个 i 预处理出 i 与其他所有点的结果

最多只可能有 n2B 种状态

预处理时间复杂度 O(MN/B)

本代码已T飞
#include<bits/stdc++.h>
#define se second
#define fi first
using namespace std;
const int N = 2e5 + 10;
typedef pair<int,int> pii;
int n,m;
vector<pii> a,e[N];
int pre[N];
map<pii,int> res;
int sum[N];
bool mark[N];
#define clamp DFJSHSFBSGDRFSRBHBFKBDSJGFDSKDSffhjffghghf
inline int clamp(int l,int r,int x){
	if(x >= r)return r;
	else if(x <= l)return l;
	return x;
}
int query(int x,int y){
	if(x > y)swap(x,y);
	if(mark[x] || mark[y])return res[{x,y}];
	
	if(e[x].empty() || e[y].empty())return res[{x,y}] = 0;
	
	int it = 0, ans = 0;
	for(pii p : e[x]){
		int l = p.fi, r = p.se;
		ans += clamp(l,r,e[y][it].se) - clamp(l,r,e[y][it].fi);
		while(it+1 < e[y].size() && e[y][it+1].fi <= r){
			++it;
			ans += clamp(l,r,e[y][it].se) - clamp(l,r,e[y][it].fi);
		}
	}
	return res[{x,y}] = ans;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1,T,P;i<=m;++i){
		scanf("%d%d",&T,&P);
		a.push_back({T,P});
		if(pre[P])e[P].push_back({pre[P],T}),pre[P] = 0;
		else pre[P] = T;
	}
	const int B = 1000;
	for(int i = 1;i<=n;++i)
		if(e[i].size() >= B){
			mark[i] = 1;
			for(int j = 1;j<=n;++j)sum[j] = 0;
			bool in = 0;
			int now = 0, pt = 0;
			for(pii p : a){
				int T = p.fi, P = p.se;
				if(P == i){
					now += in * (T - pt);
					pt = T; in ^= 1;
				}
				sum[P] = now + in * (T - pt) - sum[P];
			}
			for(int j = 1;j<=n;++i)res[{min(i,j),max(i,j)}] = sum[j];
		}
	int q,x,y; scanf("%d",&q);
	while(q--){
		scanf("%d%d",&x,&y);
		printf("%d\n",query(x,y));
	}
	return 0;
}
posted @   Luzexxi  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示