2333: [SCOI2011]棘手的操作
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1554 Solved: 588
[ Submit][ Status][ Discuss]
Description
有N个节点,标号从1到N,这N个节点一开始相互不连通。第i个节点的初始权值为a[i],接下来有如下一些操作:
U x y: 加一条边,连接第x个节点和第y个节点
A1 x v: 将第x个节点的权值增加v
A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v
A3 v: 将所有节点的权值都增加v
F1 x: 输出第x个节点当前的权值
F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值
F3: 输出所有节点中,权值最大的节点的权值
Input
输入的第一行是一个整数N,代表节点个数。
接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。
再下一行输入一个整数Q,代表接下来的操作数。
最后输入Q行,每行的格式如题目描述所示。
Output
对于操作F1, F2, F3,输出对应的结果,每个结果占一行。
Sample Input
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3
Sample Output
-10
10
10
HINT
对于30%的数据,保证 N<=100,Q<=10000
对于80%的数据,保证 N<=100000,Q<=100000
对于100%的数据,保证 N<=300000,Q<=300000
对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000
Source
题解:线段树+离线操作。
这道题貌似是可以用可并堆或者堆套堆的,但是我太水了,并不会,只好用线段树来搞一搞啦。
先离线所有询问,对于所有的U操作先进性预处理,按照读入的顺序用并查集把一个连通块内的点并到一起,并不断的更新每个连通块的最后一个节点。然后按照每个连通块的顺序,把同一个连通块中的节点放到一起,然后用线段树维护。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 300003
#define inf 1000000000
using namespace std;
int n,m;
int tr[N*4],delta[N*4],maxn[N*4],a[N];
int op[N],x[N],v[N],sz,pos[N],val[N];
int fa[N],end[N],next[N];
void init()
{
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
char s[10]; scanf("%s",s);
if (s[0]=='A')
{
if (s[1]=='1')
op[i]=2,scanf("%d%d",&x[i],&v[i]);// 将第x个节点的权值增加v
if (s[1]=='2')
op[i]=3,scanf("%d%d",&x[i],&v[i]);//将第x个节点所在的连通块的所有节点的权值都增加v
if (s[1]=='3')
op[i]=4,scanf("%d%d",&v[i]);//将所有节点的权值都增加v
}
if (s[0]=='U')
op[i]=1,scanf("%d%d",&x[i],&v[i]);//加一条边,连接第x个节点和第y个节点
if (s[0]=='F')
{
if (s[1]=='1')
op[i]=5,scanf("%d",&x[i]);//输出第x个节点当前的权值
if (s[1]=='2')
op[i]=6,scanf("%d",&x[i]);//输出第x个节点所在的连通块中,权值最大的节点的权值
if (s[1]=='3')
op[i]=7;// 输出所有节点中,权值最大的节点的权值
}
}
}
int find(int x)
{
if (fa[x]==x)
return x;
fa[x]=find(fa[x]);
return fa[x];
}
void update(int x)
{
tr[x]=max(tr[x<<1],tr[x<<1|1]);
}
void pushdown(int x)
{
if (!delta[x]) return;
delta[x<<1]+=delta[x];
delta[x<<1|1]+=delta[x];
tr[x<<1]+=delta[x];
tr[x<<1|1]+=delta[x];
delta[x]=0;
}
void build(int now,int l,int r)
{
if (l==r)
{
tr[now]=val[l];
return;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void pointchange(int now,int l,int r,int x,int v)
{
if (l==r)
{
tr[now]+=v;
return;
}
pushdown(now);
int mid=(l+r)/2;
if (x<=mid)
pointchange(now<<1,l,mid,x,v);
else
pointchange(now<<1|1,mid+1,r,x,v);
update(now);
}
int findpoint(int now,int l,int r,int x)
{
if (l==r) return tr[now];
pushdown(now);
int mid=(l+r)/2;
if (x<=mid)
findpoint(now<<1,l,mid,x);
else
findpoint(now<<1|1,mid+1,r,x);
}
void query(int now,int l,int r,int ll,int rr,int x)
{
if (l>=ll&&r<=rr)
{
tr[now]+=x;
delta[now]+=x;
return;
}
pushdown(now);
int mid=(l+r)/2;
if (ll<=mid)
query(now<<1,l,mid,ll,rr,x);
if (rr>mid)
query(now<<1|1,mid+1,r,ll,rr,x);
update(now);
}
int qjmax(int now,int l,int r,int ll,int rr)
{
if (l>=ll&&r<=rr)
return tr[now];
pushdown(now);
int mid=(l+r)/2;
int ans=-inf;
if (ll<=mid)
ans=max(ans,qjmax(now<<1,l,mid,ll,rr));
if (rr>mid)
ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr));
return ans;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
init();
for (int i=1;i<=n;i++)
fa[i]=i,end[i]=i;
for (int i=1;i<=m;i++)
if (op[i]==1)
{
int r1=find(x[i]),r2=find(v[i]);
if (r1==r2) continue;
fa[r2]=r1;//并查集的代表节点
next[end[r1]]=r2;//用next数组连通块中的点连接
end[r1]=end[r2];//记录连通块的结束节点
}
for (int i=1;i<=n;i++)
if (find(i)==i)
{
for (int j=i;j;j=next[j])
sz++,pos[j]=sz,val[sz]=a[j];
}
build(1,1,n);
for (int i=1;i<=n;i++)
fa[i]=i,end[i]=i;
for (int i=1;i<=m;i++)
{
if (op[i]==1)
{
int r1=find(x[i]),r2=find(v[i]);
if (r1==r2) continue;
fa[r2]=r1;
end[r1]=end[r2];
}
if (op[i]==2)
pointchange(1,1,n,pos[x[i]],v[i]);
if (op[i]==3)
query(1,1,n,pos[find(x[i])],pos[end[find(x[i])]],v[i]);
if (op[i]==4)
query(1,1,n,1,n,v[i]);
if (op[i]==5)
printf("%d\n",findpoint(1,1,n,pos[x[i]]));
if (op[i]==6)
printf("%d\n",qjmax(1,1,n,pos[find(x[i])],pos[end[find(x[i])]]));
if (op[i]==7)
printf("%d\n",qjmax(1,1,n,1,n));
}
}
题解:可并堆+set
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define N 300003
#define inf 1000000000
using namespace std;
int fa[N],lch[N],rch[N],size[N],val[N],delta[N],d[N];
int f[N];
int n,m,mx,sum;
multiset<int> p;
int find(int x)
{
if (fa[x]==x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
void pushdown(int now)
{
int l=lch[now]; int r=rch[now];
if (delta[now]) {
if(l) val[l]+=delta[now],delta[l]+=delta[now];
if(r) val[r]+=delta[now],delta[r]+=delta[now];
delta[now]=0;
}
}
int merge(int x,int y)
{
if (!x) return y;
if (!y) return x;
if (val[x]<val[y]) swap(x,y);
pushdown(x);
int t=merge(rch[x],y);
f[t]=x; rch[x]=t;
if (d[lch[x]]<d[rch[x]]) swap(lch[x],rch[x]);
d[x]=d[rch[x]]+1;
return x;
}
int get(int x)
{
return lch[f[x]]==x;
}
void solve(int x,int r1)
{
if (!lch[x]&&!rch[x]) {
if (x==r1) {
p.insert(val[x]);
return;
}
int which=get(x);
if (which) lch[f[x]]=0;
else rch[f[x]]=0;
f[x]=lch[x]=rch[x]=0;
fa[r1]=fa[x]=merge(r1,x);
if(x==fa[r1]) {
p.erase(p.find(val[r1]));
p.insert(val[x]);
}
return;
}
int l=lch[x]; int r=rch[x];
pushdown(x);
int which=get(x);
int t=merge(l,r);
if (x==r1) {
f[x]=lch[x]=rch[x]=0; f[t]=0;
p.insert(val[t]);
fa[r1]=fa[t]=merge(t,x);
if(x==fa[r1]) {
p.erase(p.find(val[t]));
p.insert(val[x]);
}
return;
}
if (which) lch[f[x]]=t,f[t]=f[x];
else rch[f[x]]=t,f[t]=f[x];
f[x]=lch[x]=rch[x]=0;
fa[r1]=fa[x]=merge(r1,x);
if (x==fa[x]) {
p.erase(p.find(val[r1]));
p.insert(val[x]);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d",&n); mx=-1000000000;
p.clear();
for (int i=1;i<=n;i++) {
scanf("%d",&val[i]);
p.insert(val[i]);
}
for (int i=1;i<=n;i++) fa[i]=i;
scanf("%d",&m);
for (int i=1;i<=m;i++) {
int opt,x,y,v; char s[10];
scanf("%s",s);
if (s[0]=='U') {
scanf("%d%d",&x,&y);
int r1=find(x); int r2=find(y);
if (r1!=r2) {
int t=merge(r1,r2);
if (t==r1) {
p.erase(p.find(val[r2])); }
else
p.erase(p.find(val[r1]));
fa[r2]=fa[r1]=t;
}
}
if (s[0]=='A'&&s[1]=='1') {
scanf("%d%d",&x,&v);
int now=f[x]; int r1=find(x); int k=0;
while (now!=0) k+=delta[now],now=f[now];
if (x==r1) p.erase(p.find(val[x]));
val[x]+=k+v;
solve(x,r1);
}
if (s[0]=='A'&&s[1]=='2') {
scanf("%d%d",&x,&v);
int r1=find(x);
p.erase(p.find(val[r1]));
val[r1]+=v; delta[r1]+=v;
p.insert(val[r1]);
mx=max(mx,val[r1]);
}
if (s[0]=='A'&&s[1]=='3') {
scanf("%d",&v); sum+=v;
}
if (s[0]=='F'&&s[1]=='1') {
scanf("%d",&x);
int now=f[x]; int r1=find(x); int k=0;
while (now!=0) k+=delta[now],now=f[now];
printf("%d\n",val[x]+sum+k);
}
if (s[0]=='F'&&s[1]=='2') {
scanf("%d",&x);
int r1=find(x);
printf("%d\n",val[r1]+sum);
}
if (s[0]=='F'&&s[1]=='3')
printf("%d\n",*--p.find(inf)+sum);
//cout<<*--p.find(inf)<<endl;
}
}