cf#809div2

ABCD1

E

给一个有序的边集合,保证集合内边能构成连通图。1e5次询问,回答[l,r]上的点两两联通所需的最小的k(仅使用编号[1,k]的所有边)

分析

一眼像一个排好序的kruskal,但显然不能负担每次询问都建树的复杂度。
转化“[l,r]上的点两两连通”这个条件,其等价于“[l,r]上相邻两点连通”(充分且必要)
题目思路就是在kruskal时,算出每个相邻点最早连通时间,只需要在并查集合并时按秩合并即可。(可以并查集维护关系,vector维护点的集合)
每次询问考虑区间上最后连通的相邻点对即可,线段树维护最大值。

code
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=2e5+110;
struct Edge{
	int u,v;
}edge[maxn];
int f[maxn];
int a[maxn];
vector<int>g[maxn];
int n,m,q;
inline void read(int &x){
	x=0;char tmp=getchar();
	while(tmp<'0'||tmp>'9')tmp=getchar();
	while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
}
int find(int x){
	if(f[x]==x)return x;
	return f[x]=find(f[x]);
}
int t[maxn<<2];
void build(int x,int l,int r){
	if(l==r){
		t[x]=a[l];
		return ;
	}
	int mid=l+r>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	t[x]=max(t[x<<1],t[x<<1|1]);
}
int query(int x,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)
		return t[x];
	int mid=l+r>>1,ans=0;
	if(ql<=mid)ans=max(ans,query(x<<1,l,mid,ql,qr));
	if(qr>mid)ans=max(ans,query(x<<1|1,mid+1,r,ql,qr));
	return ans;
}
signed main(){
//	freopen("stdout.txt","w",stdout);
	int T;cin>>T;
	while(T--){
		cin>>n>>m>>q;
		for(int i=1;i<=n;i++)
			f[i]=i,a[i]=0,g[i].resize(0),g[i].push_back(i) ;
		for(int i=1;i<=m;i++){
			read(edge[i].u),read(edge[i].v);
		}
		for(int i=1;i<=m;i++){
			int u=find(edge[i].u),v=find(edge[i].v);
			if(u==v)continue;
			if(g[u].size()>g[v].size())swap(u,v);
			f[u]=v;
			for(auto r:g[u]){
				if(!a[r]){
					if(find(r+1)==find(r))a[r]=i;
				}
				if(r>1&&!a[r-1])
					if(find(r-1)==find(r))a[r-1]=i;
				g[v].push_back(r);
			}
			g[u].resize(0);
		}
		n--;
		build(1,1,n);
		int l,r;
		while(q--){
			read(l),read(r);
			if(l==r)printf("0 ");
			else printf("%d ",query(1,1,n,l,r-1));
		}
		puts("");
	}
	return 0;
}

D2

对于n个数从小到大的\(a_i,b_i=\lfloor \frac{a_i}{j}\rfloor(1<=j<k),j\)不固定。问\(b\)数组的最小的极差。
\(n,k,a_i<=1e5\)

分析

D1为其弱化版,可以\(n^2\)暴力。考场上D2时间不够了,没想到正解。
有个显然的想法是,对于每个\(b_i\)至多有\(O(\sqrt{n})\)个值。(富比尼定理)
所以可以考虑整除分块的做法。
如果我们枚举\(b_i\)的最小值v,那么当前\(b_i\)最优的解一定是大于等于v的第一个\(\lfloor \frac{a_i}{j}\rfloor\)
v对应的最大值则是这一系列后继中最大的那一个。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline void read(int &x){
	x=0;int fl=1;char tmp=getchar();
	while(tmp<'0'||tmp>'9')fl=tmp=='-'?-fl:fl,tmp=getchar();
	while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
	x=x*fl;
}
const int maxn=1e5+1000;
int n,k;
int a[maxn];
int p[maxn];
signed main(){
	int T;cin>>T;
	while(T--){
		cin>>n>>k;
		memset(p,0,sizeof p);
		for(int i=1;i<=n;i++)
			read(a[i]);
		for(int i=1;i<=n;i++){
			int mx=1e9;
			for(int j=1;j<=min(a[i],k);j++){
				int val=a[i]/j;
				p[val+1]=max(p[val+1],mx);
				mx=val,j=a[i]/val;
			}
			p[0]=mx;
		}
		int ans=1e9,mx=0;
		for(int i=0;i<=a[1];i++){
			mx=max(mx,p[i]);
			ans=min(ans,mx-i);
		}
		cout<<ans<<endl;
	}
    return 0;
}
posted @ 2022-07-20 22:52  xyc1719  阅读(33)  评论(0)    收藏  举报