fzu 2059 并查集+离线处理

题意:

There is a array contain N(1<N<=100000) numbers. Now give you M(1<M<10000) query.

Every query will be:

1 x : ask longest substring which every number no less than x

2 y x : change the A[y] to x. there are at most change 10 times.

For each ask can you tell me the length of longest substring.

思路:

简单题,因为修改的次数很少。

对于确定的n个数字,可以先把n个数字从小到大排序,然后从大到小依次进行线段块的合并(如果一个点左右的线段块都已经访问过了,说明大小满足覆盖要求),这很适合并查集的特点,所以可以用并查集加个权值,来记录每一块的线段长度。

然后每一次修改之后,就重新进行上面过程。然后之后的每一个查询,都是二分查表即可。(这个地方我想是可以有O(1)的处理方式的)

code:

#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
#include<map>
#include<set>
#include<cmath>
#include<cctype>
#include<cstdlib>
using namespace std;

#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define mem(a, b) memset(a, b, sizeof(a))
#define mod 1000000007
typedef pair<int,int> pii;
typedef long long LL;
//------------------------------
const int maxn = 100005;
const int maxm = 10005;

int n,m;
int x[maxm], a[maxn];
struct node{
    int num, id, ans;
    bool operator < (const node nt) const{
        return num < nt.num;
    }
}b[maxn];

int p[maxn], len[maxn];

int find(int x){
    if(x == p[x]) return p[x];
    else return p[x] = find(p[x]);
}
void union_(int u, int v){
    int pu = find(u);
    int pv = find(v);
    if(pu != pv){
        p[pu] = pv;
        len[pv] += len[pu];
    }
}
void deal(){
    for(int i = 1; i <= n; i++){
        b[i].num = a[i];
        b[i].id = i;
    }
    sort(b+1, b+1+n);

    int max_ = 0;
    memset(len, 0, sizeof(len));
    for(int i = 1; i <= n; i++) p[i] = i;
    for(int i = n; i >= 1; i--){
        int id = b[i].id;
        len[id] = 1;
        if(len[id-1]) union_(id-1, id);
        if(len[id+1]) union_(id+1, id);
        if(max_ < len[find(id)]) max_ = len[find(id)];
        b[i].ans = max_;
    }
//    for(int i = 1; i <= n; i++){
//        printf("x = %d   len = %d\n",b[i].num, b[i].ans);
//    }
}
int get_ans(int x){
    node tmp;
    tmp.num = x;
    int id = lower_bound(b+1, b+1+n, tmp) - b;
//    printf("x = %d id = %d\n",x,id);
    return b[id].ans;
}
void solve(){
    int op, a1, a2;
    deal();
    for(int i = 1; i <= m; i++){
        scanf("%d",&op);
        if(op == 2){
            scanf("%d%d",&a1,&a2);
            a[a1] = a2;
            deal();
        }
        if(op == 1){
            scanf("%d",&a1);
            if(a1 > b[n].num){
                printf("0\n");
                continue;
            }
            int ans = get_ans(a1);
            printf("%d\n",ans);
        }
    }
}
int main(){
    while(scanf("%d%d",&n,&m) != EOF){
        for(int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
        }
        solve();
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值