【codechef】Ancient Berland Roads(线段树)

题目描述

在古老的宝兰国有 N 座城市和 M 条双向道路。随着时间的流逝,一些道路渐渐变得破旧,最后因为损坏而无法使用,而从来没有人维修这些道路。作为古宝兰国历史的热爱者,你想要做一个小小的研究。为此,你想要写一个程序来处理以下的操作:

• D K,代表输入中的第 K 条道路损坏了。道路从 1 开始编号;

• P A B,代表编号为 A 的城市的人口数变为了 B。

我们称一些城市构成的集合为一个区域,当且仅当对于集合中的任意两座城市,都可以通过没有损坏的道路,仅经过集合中的城市互相到达。区域的人口数即为集合中所有城市的人口数之和。

给定初始的道路、人口数,以及 M 个操作。你的任务就是,在每次询问后输出人口数最大的区域中有多少个城市。

输入格式

输入数据的第一行包含三个整数 N、M 和 Q,分别代表城市数、道路数和操作数。接下来一行包含 N 个整数 P1, P2, . . . , PN,以空格隔开,代表每个城市的人口数。接下来 M 行,每行包含两个整数 Xj 和 Yj,代表编号为 Xj 和 Yj 之间有一条双向道路。接下来 Q 行,每行包含一个操作,格式如题目描述中所述。

输出格式

输出 Q 行,依次代表每次操作后人口数最多的区域的大小。


并查集。既然它是删边那我就反过来倒着加边。30分。(树状数组说好的logn复杂度?但求区间最值好像不止这点。。。

数列每次修改一个数值求最大值?看了大神代码果然用优先队列过的(然而并不理解为什么可以用while过滤。。。

然后注意一下以后在codechef上提交都用lld,不然会WA

#include<iostream>  
#include<algorithm>  
#include<string>  
#include<map>//ll dx[4]={0,0,-1,1};ll dy[4]={-1,1,0,0};  
#include<set>//  
#include<vector>  
#include<cmath>  
#include<stack>  
#include<string.h>  
#include<stdlib.h>  
#include<cstdio>   
#define ll long long
#define lowbit(x) (x) & (-x)
using namespace std;
map<char,int> r[27];
struct node{
	int a,b,c;
}x[500005];
struct node2{
	char a;
	int b,c,d;
}y[500005];
int fa[500005];
int n,m,q;
int find(int v){  
    if (fa[v] == v)  
        return v;  
    else  
        return fa[v]=find(fa[v]);  
}  
int g[500005];
int c2[500010],w[500005];
void update2(int ii,int val){ //修改点       
    w[ii]=val;  
    for(int i=ii; i<=n+5; i+=lowbit(i)){       
        if(val>c2[i])  
            c2[i]=val;  
        else  
            break;  
    }        
}        
int query(int l, int r){  
    int s=w[r];//上边界  
    while (l!=r){  
        for(r-=1;r-lowbit(r)>=l;r-=lowbit(r)){    
            s=max(s,c2[r]);//注意计算区间,不要夸区间  
        }    
        s=max(s,w[r]);  //下边界  
    }  
    return s;  
}   
int main(){    
	scanf("%d%d%d",&n,&m,&q);
	for(int i=0;i<=n;++i){
		fa[i]=i;
	}
	for(int i=1;i<=n;++i){
		scanf("%d",&g[i]);
		update2(i,g[i]);
	}
	for(int i=1;i<=m;++i){
		scanf("%d%d",&x[i].a,&x[i].b);
		x[i].c=0;
	}
	for(int i=0;i<q;++i){
		getchar();
		scanf("%c",&y[i].a);
		if(y[i].a=='D'){
			scanf("%d",&y[i].b);
			x[y[i].b].c=1;
		}
		else{
			scanf("%d%d",&y[i].b,&y[i].c);
			int tmp=y[i].c;
			y[i].c=g[y[i].b];
			g[y[i].b]=tmp;
			update2(y[i].b,tmp);
		}
	}
	int maxx=0,ss=0;
	for(int i=1;i<=m;++i){
		if(x[i].c==1){
			maxx=max(w[x[i].a],maxx);
			maxx=max(w[x[i].b],maxx);
			continue;
		}
		int aa=find(x[i].a),bb=find(x[i].b);
		if(aa!=bb){
			fa[aa]=bb;
			update2(bb,w[bb]+w[aa]);
			update2(aa,0);
			if(w[bb]>maxx){
				maxx=w[bb];
			}
		}
	}
	for(int i=q-1;i>=0;--i){
		y[i].d=query(1,n);
		if(y[i].a=='D'){
			int p=y[i].b;
			int aa=find(x[p].a),bb=find(x[p].b);
			if(aa!=bb){
				fa[aa]=bb;
				update2(bb,w[bb]+w[aa]);
				update2(aa,0);
			}
		}
		else{
			int p=y[i].b;
			int aa=find(p);
			update2(aa,w[aa]+y[i].c-g[p]);
			g[p]=y[i].c;
		}	
	}
	for(int i=0;i<q;++i)
		printf("%d\n",y[i].d);
}  

【线段树版100分代码】

#include<iostream>  
#include<algorithm>  
#include<string>  
#include<map>//ll dx[4]={0,0,-1,1};ll dy[4]={-1,1,0,0};  
#include<set>//  
#include<vector>  
#include<cmath>  
#include<stack>  
#include<string.h>  
#include<stdlib.h>  
#include<cstdio>   
#define ll long long
#define MAXM 500005
#define lowbit(x) (x) & (-x)
using namespace std;
struct node{
	int a,b,c;
}x[500005];
struct node2{
	char a;
	int b;
	ll c,d;
}y[500005];
int fa[500005];
int n,m,q;
int find(int v){  
    if (fa[v] == v)  
        return v;  
    else  
        return fa[v]=find(fa[v]);  
}  
ll g[500005];
ll w[500005];
struct SegTree //求最值
{
	struct node
	{
		long long num, sum;
	}tree[MAXM << 2];
	int L, R;
	long long V;
	void pushDown(int u, int l, int r, int m)//向下更新节点(节省时间)
	{
		if (tree[u].num != 0)
		{
			node &lson = tree[u << 1], &rson = tree[u << 1 | 1];
			lson.num += tree[u].num;
			rson.num += tree[u].num;
			tree[u].sum += tree[u].num;
			tree[u].num = 0;
		}
	}

	void pushUp(int u) //【改】 
	{   
		tree[u].sum = max(tree[u << 1].sum + tree[u << 1].num, tree[u << 1 | 1].sum + tree[u << 1 | 1].num);
	}
	void built(int u, int l, int r)
	{
		tree[u].num = 0;
		if (l == r)
		{
			tree[u].sum = 0;
			return;
		}
		int mid = l + r >> 1;
		built(u << 1, l, mid);
		built(u << 1 | 1, mid + 1, r);
		pushUp(u);
	}

	void _add(int u, int l, int r)//区间或者点加值
	{
		int mid = l + r >> 1;
		if (L <= l&&r <= R)
		{
			tree[u].num += V; //【改,求和是V*(r-l+1)】 //【改=会错】 
			pushDown(u, l, r, mid);
			return;
		}
		pushDown(u, l, r, mid);
		if (L <= mid)
			_add(u << 1, l, mid);
		if (R>mid)
			_add(u << 1 | 1, mid + 1, r);
		pushUp(u);
	}

	long long _query(int u, int l, int r)
	{
		int mid = l + r >> 1;
		if (L <= l&&r <= R)
		{
			pushDown(u, l, r, mid);
			return tree[u].sum;
		}
		pushDown(u, l, r, mid);
		long long cnt = 0;
		if (L <= mid)
			cnt = max(cnt, _query(u << 1, l, mid)); //【改】 
		if (mid<R) 
			cnt = max(cnt, _query(u << 1 | 1, mid + 1, r)); //【改】 
		pushUp(u);
		return cnt;
	}

	void add(int l, int r, long long v)
	{
		L = l;
		R = r;
		V = v;
		_add(1, 1, n);
	}
	long long query(int l, int r)
	{
		if (l <= r)
		{
			L = l, R = r;
			return _query(1, 1, n);
		}
		else
			return 0;
	}
}A;
int main(){    
	scanf("%d%d%d",&n,&m,&q);
	for(int i=0;i<=n;++i){
		fa[i]=i;
	}
	for(int i=1;i<=n;++i){
		scanf("%lld",&g[i]);  //每个城市的人口数 
		w[i]=g[i];
		A.add(i,i,g[i]);
	}
	for(int i=1;i<=m;++i){
		scanf("%d%d",&x[i].a,&x[i].b);  //编号之间有一条双向道路 
	}
	for(int i=0;i<q;++i){
		getchar();
		scanf("%c",&y[i].a);
		if(y[i].a=='D'){
			scanf("%d",&y[i].b);  //第 K 条道路损坏了
			x[y[i].b].c=1;
		}
		else{
			scanf("%d%lld",&y[i].b,&y[i].c);  //编号为 A 的城市的人口数变为了 B
			ll tmp=y[i].c;
			A.add(y[i].b,y[i].b,tmp-g[y[i].b]);
			y[i].c=g[y[i].b];
			g[y[i].b]=tmp;
			w[y[i].b]=tmp;
		}
	}
	for(int i=1;i<=m;++i){
		if(x[i].c==1)
			continue;
		int aa=find(x[i].a),bb=find(x[i].b);
		if(aa!=bb){
			fa[aa]=bb;
			A.add(bb,bb,w[aa]);
			A.add(aa,aa,-w[aa]);
			w[bb]+=w[aa];
			w[aa]=0;
		}
	} 
	for(int i=q-1;i>=0;--i){
		y[i].d=A.query(1,n);
		if(y[i].a=='D'){
			int p=y[i].b;
			int aa=find(x[p].a),bb=find(x[p].b);
			if(aa!=bb){
				fa[aa]=bb;
				A.add(bb,bb,w[aa]);
				A.add(aa,aa,-w[aa]);
				w[bb]+=w[aa];
				w[aa]=0;
			}
		}
		else{
			int p=y[i].b;
			int aa=find(p);
			A.add(aa,aa,y[i].c-g[p]);
			w[aa]+=y[i].c-g[p];
			g[p]=y[i].c;
		}
	}
	for(int i=0;i<q;++i)
		printf("%lld\n",y[i].d);
	return 0;
}  

用set的方法:

每次要合并就先把set里面的a和b先删掉(用pair存,默认以first优先排序),再ab合并后把新的根节点和值重新存进去。

更改点:先把set里面的这个点删掉(搜根节点),重新计算点值后存入

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 5e5 + 5;

int type,q,u,v,m;
ll val[N];
pair < int ,int > edges[N];
int uf[N];
ll prev[N];
int good[N];
int a[N];
int b[N];
ll ans[N];
string cur[N];

set < pair < ll , int > , greater < pair < ll , int > > > DSU;



ll QUERY(){
    set < pair < ll , int > > :: iterator it = DSU.begin();
    return it->first;
}


int FIND(int u)
{
    if(uf[u]!=uf[uf[u]]){
        uf[u] = FIND(uf[u]);
    }
    return uf[u];
}


void UNION(int u , int v)
{
    int xx = FIND(u);
    int yy = FIND(v);
    if(xx==yy) return;
    DSU.erase(make_pair(val[xx],xx));
    DSU.erase(make_pair(val[yy],yy));
    uf[yy] = xx;
    val[xx] += val[yy];
    DSU.insert(make_pair(val[xx],xx));
}




int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t,n;
    cin >> n >> m >> q;
    for(int i = 1; i<=n; ++i){
        cin >> val[i];
        uf[i] = i;
    }
    for(int i = 1; i<=m; ++i){
        cin >> u >> v;
        edges[i] = make_pair(u,v);
        good[i] = true;
    }

    for(int i = 1; i<=q; ++i){
        cin >> cur[i];
        if(cur[i][0]=='D'){
            cin >> a[i];
            good[a[i]] = false;
        }
        else{
            cin >> a[i] >> b[i];
            prev[i] = val[a[i]];
            val[a[i]] = b[i];
        }
    }

    for(int i = 1; i<=n; ++i){
        DSU.insert(make_pair(1LL * val[i],i));
    }
    for(int i = 1; i<=m; ++i){
        if(good[i]){
            UNION(edges[i].first,edges[i].second);
        }
    }

    ans[q] = QUERY();
    for(int i = q; i > 0; --i){
        if(cur[i][0]=='D'){
            UNION(edges[a[i]].first,edges[a[i]].second);
        }
        else{
            int ind = a[i];
            int newval = b[i];
            int leader = FIND(ind);
            DSU.erase(make_pair(val[leader],leader));
            val[leader] -= newval;
            val[leader] += prev[i];
            DSU.insert(make_pair(val[leader],leader));
        }
        ans[i-1] = QUERY();
    }

    for(int i = 1; i<=q; ++i){
        cout << ans[i] << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值