codeforces div.1 C. Fixed Point Removal

C. Fixed Point Removal

题目链接

题目大意

给一个数组,如果ai == i 那就可以删除ai , 之后后面的就可以并上来,也就是后面的下标都-1.
问最多可以删除多少个。
有m组询问 每次询问输入 x,y 就是 把前x个和后y个变成n + 1, 后,答案是多少。

瞎bb

单纯理一下思路。。
如果没有查询
可以注意到删掉后面的,对前面的没有影响。如果当前的 ai > i 那必不可能删除这个值了。所以应该从后往前删,如果当前的 ai <= i 并且 前面删除的个数大于等于i - ai 那这个值就一定可以删除。
有查询
有查询操作怎么办?
把前x个后y个变成n + 1也就是前x 个和后y个不能删除了, 所以就是只考虑x + 1 ~ n - y + 1区间里的数。
考虑可以删除这个点的时候左端点的最大值是多少?
为什么考虑这个东西? 因为这个东西是单调的。
也就是说: 如果左端点是2的时候,可以删除这个点。那么左端点是1的时候,一定可以删除这个点。这个应该很好理解。。因为只要前面可以删除的数量大于等于i - a[i] 就好了, 如果2 ~ i - 1删除的数量大于等于 i - a[i] 那么1 ~ i - 1可以删除的数量就肯定大于等于i - a[i]
那么这个题就比较好做了,离线,按询问的 r 排个序,二分找出可以删除这个点的最左端的下标,然后把前面的下标都+1,就好。
询问的时候直接询问l点的权值。
区间修改(变成单点修改),单点询问,可以树状数组。

#include<algorithm>
#include<iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <stack>
#include <map>
#include <bitset>
#include <set>
#include <unordered_set>
#include <climits>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef unordered_set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b > 0){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}};
int lb(int x){return  x & -x;}
//friend bool operator < (Node a,Node b)   重载
const int inf = INT_MAX;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9+7;
const int maxn = 4e5+10;
const int M = 1e6+10;
int ans[maxn];
int tree[maxn];
int n;
int a[maxn];
std::vector<pii> vv[maxn];
void add(int x,int num)
{
    while(x <= n)
    {
        tree[x] += num;
        x += lb(x);
    }
}
int query(int x)
{
    int ans =0 ;
    while(x > 0)
    {
        ans += tree[x];
        x -= lb(x);
    }
    return ans;
}

int main()
{
    int m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i ++ )
        scanf("%d",&a[i]);
    for (int i = 1; i <= m; i ++ )
    {
        int x,y;
        scanf("%d %d",&x,&y);
        vv[n - y].pb(mkp(x + 1,i));
    }
    for(int i = 1; i <= n; i ++ )
    {
        a[i] = i - a[i];
    }
    for (int i = 1; i <= n; i ++ )
    {
        if(a[i] >= 0)
        {
            // printf("123\n");
            int l = 1, r = i;
            while(l < r)
            {
                int mid = l + r + 1 >> 1;
                if(query(mid) >= a[i])
                    l = mid;
                else
                    r = mid - 1;
            }
            // printf("%d\n", l);
            if(query(l) >= a[i])
            {
                add(1,1);
                add(l + 1,-1);
                // printf("111\n");
            }
        }
        // for (int j =1 ; j<= n; j ++ )
        //     printf("%d ",query(j));
        // printf("\n");
        for (int j = 0; j < vv[i].size(); j ++ )
        {
            ans[vv[i][j].sd] = query(vv[i][j].st);
            // printf("%d %d\n",vv[i][j].st,vv[i][j].sd);
        }
        // printf("11111111111\n");
    }
    for (int i = 1; i <= m; i ++ )
        printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值