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

浙公网安备 33010602011771号