纪念下cf第一次ak
E1. Array and Segments (Easy version)
题意:有一个长度为n的序列a,有m次操作,每个操作有个区间可以使得序列a在该区间所有数减一,你可以任意顺序任意选择一些操作,使得序列a(最大值-最小值)最大,求这个最大的值。
思路:我首先把所有区间按照左区间从小到大排序(左区间相同,右区间从小到大排序),然后一个一个暴力更新区间的值,更新(max-min)的值并更新到答案,算完之后再把所有区间按照右区间从大到小排序(右区间相同,左区间从大到小排序),然后同上操作,就可以求出答案了。
update:有群友问我证明,我证明了发现我排序还排复杂了(还可以更简单),第一遍排序为y从小到大排序,第二遍排序为x从大到小排序,假设经过一系列操作后,最优答案是abs(ai-aj),假设aj>ai,那么经过第一类排序,对于所有的[ x , y ](x<=i<=y<j)更新就可取得答案,是不是按照第一类排序顺序就可以做到,假设ai>aj,经过第二类排序,对于所有的[ x , y ](i<x<=j<=y)更新就可以取得答案,我们按照第二类排序顺序就可以做到。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=305;
int n,m,a[maxn],b[maxn],c[maxn],cnt,ans=-1;
struct node
{
int x,y,id;
}q[maxn];
bool cmp1(node a,node b)
{
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
bool cmp2(node a,node b)
{
if(a.y==b.y)return a.x>b.x;
return a.y>b.y;
}
void getb()
{
for(int i=1;i<=n;i++)
b[i]=a[i];
}
int getv()
{
int mx=-1e9,mn=1e9;
for(int i=1;i<=n;i++)
mx=max(mx,b[i]),mn=min(mn,b[i]);
return mx-mn;
}
void upans(int res,int tot)
{
if(res>ans)
{
ans=res;
cnt=tot;
for(int i=1;i<=cnt;i++)
c[i]=q[i].id;
}
}
void upb(int l,int r)
{
for(int i=l;i<=r;i++)
b[i]--;
}
void work()
{
int res=getv();
upans(res,0);
for(int i=1;i<=m;i++)
{
upb(q[i].x,q[i].y);
res=getv();
upans(res,i);
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++)
cin>>q[i].x>>q[i].y,q[i].id=i;
sort(q+1,q+1+m,cmp1);
getb();
work();
sort(q+1,q+1+m,cmp2);
getb();
work();
cout<<ans<<endl;
printf("%d\n",cnt);
for(int i=1;i<cnt;i++)printf("%d ",c[i]);
if(cnt)
printf("%d\n",c[cnt]);
}
E2. Array and Segments (Hard version)
和上题一样,不过n的范围变成了1e5,但是没关系,上个题的代码,数组大小改成1e5照样过,不过我还是写个线段树优化吧,求区间最值,并且区间减一,这个是最简单的lazy型线段树了吧(直接上代码了)..........
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n,m,a[maxn],c[maxn],cnt,ans=-1;
int mx[maxn*4],mn[maxn*4],tag[maxn*4];
struct node
{
int x,y,id;
}q[maxn];
bool cmp1(node a,node b)
{
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
bool cmp2(node a,node b)
{
if(a.y==b.y)return a.x>b.x;
return a.y>b.y;
}
void build(int o,int l,int r)
{
if(l==r)
{
mx[o]=mn[o]=a[l];
tag[o]=0;
return;
}
int mid=(l+r)/2,ls=o*2,rs=o*2+1;
build(ls,l,mid);
build(rs,mid+1,r);
tag[o]=0;
mx[o]=max(mx[ls],mx[rs]);
mn[o]=min(mn[ls],mn[rs]);
}
void up(int o,int l,int r,int ql,int qr)
{
int mid=(l+r)/2,ls=o*2,rs=o*2+1;
if(l>=ql&&r<=qr)
{
mx[o]--,mn[o]--;
tag[o]--;
return;
}
if(ql<=mid)up(ls,l,mid,ql,qr);
if(qr>mid)up(rs,mid+1,r,ql,qr);
mx[o]=max(mx[ls],mx[rs])+tag[o];
mn[o]=min(mn[ls],mn[rs])+tag[o];
}
void work()
{
int tmp=0;
if(mx[1]-mn[1]>ans)
tmp=0,ans=mx[1]-mn[1];
for(int i=1;i<=m;i++)
{
up(1,1,n,q[i].x,q[i].y);
if(mx[1]-mn[1]>ans)
tmp=i,ans=mx[1]-mn[1];
}
if(tmp)
{
cnt=tmp;
for(int i=1;i<=tmp;i++)
c[i]=q[i].id;
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++)
cin>>q[i].x>>q[i].y,q[i].id=i;
sort(q+1,q+1+m,cmp1);
build(1,1,n);
work();
sort(q+1,q+1+m,cmp2);
build(1,1,n);
work();
cout<<ans<<endl;
printf("%d\n",cnt);
for(int i=1;i<cnt;i++)printf("%d ",c[i]);
if(cnt)
printf("%d\n",c[cnt]);
}
题意:给你一个无向带权图,每次操作你可以把某些边的权值+1,求最少的操作次数使得该无向图最小生成树唯一。
思路:首先求出最小生成树并且标记用过的边,然后枚举没用过的边u v w,我先在树上用倍增算法找到u v路径上最大的边权mx,如果mx等于w,那么是不是我可以把那条mx的边删除,用这条边替代,照样可以联通这颗树,且最小生成树权值不变,也就是说,最小生成树不唯一了,那么这条边就必须要改变权值。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+50;
struct node
{
int u,v,w;
bool operator<(const node&t)const
{
return w<t.w;
}
}a[maxn];
int par[maxn],vis[maxn],n,m,ans;
int f[maxn][21],mx[maxn][21],dep[maxn];
vector<int>G[maxn],dis[maxn];
int find(int x)
{
if(x!=par[x])
par[x]=find(par[x]);
return par[x];
}
void kruskal()
{
sort(a+1,a+1+m);
int res=0;
for(int i=1;i<=m;i++)
{
int u=a[i].u,v=a[i].v;
int fu=find(u),fv=find(v);
if(fu!=fv)
{
par[fu]=fv;
vis[i]=1;
G[u].push_back(v);
G[v].push_back(u);
dis[u].push_back(a[i].w);
dis[v].push_back(a[i].w);
res++;
if(res==n-1)break;
}
}
}
void dfs(int u,int fa,int w,int deep)
{
f[u][0]=fa;
mx[u][0]=w;
dep[u]=deep;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa)continue;
dfs(v,u,dis[u][i],deep+1);
}
}
void init()
{
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
{
int fa=f[j][i-1];
f[j][i]=f[fa][i-1];
mx[j][i]=max(mx[j][i-1],mx[fa][i-1]);
}
}
int lca(int p,int q)
{
if(dep[p]<dep[q])swap(p,q);
int res=0;
for(int i=20;i>=0;i--)
if(dep[f[p][i]]>=dep[q])
res=max(res,mx[p][i]),p=f[p][i];
if(p==q)return res;
for(int i=20;i>=0;i--)
if(f[p][i]!=f[q][i])
{
res=max(res,mx[p][i]);
res=max(res,mx[q][i]);
p=f[p][i],q=f[q][i];
}
return max(res,max(mx[p][0],mx[q][0]));
}
int work()
{
for(int i=1;i<=m;i++)
if(!vis[i])
{
int w=lca(a[i].u,a[i].v);
if(w==a[i].w)
ans++;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
par[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
kruskal();
dfs(1,0,0,1);
init();
printf("%d\n",work());
}