E. Level Up

zouyua發表於2024-08-01

E. Level Up

  • 題意
    玩家初始等級為 \(1\), 有 \(n\) 只怪物,每個怪物有一個等級 \(a_i\), 如果怪物等級高於你,則你們會戰鬥,戰鬥後經驗加1,否則怪物會逃跑,你不會獲得經驗,每 k 點經驗就會升級。給你 \(q\) 個詢問,給個詢問給出 \(i,x\), 問你當 \(k = x\) 時,會不會發生戰鬥(即問你你的等級會不會小於等於此時的\(a_i\)
  • \(1≤n,q≤2⋅10^5, 1≤ai≤2⋅10^5, 1≤i,x≤ n\)

聽說根號分治的 sqrt 會被 hack,這裡講一個\(nlognlogn\)的做法

可以根據\(q\),問的是到\(i\)\(k = x\)時的\(lv\)會不會小於等於\(a_i\), 因為lv與k是反比例關係,k越小,lv就會越高,對於每個位置處理出如果能發生戰鬥所需要的最大的k,記為\(b_i\), 如過\(b_i <= x\), 則可以說明,比能發生戰鬥所需的k還大,則到這個位置的lv會比\(a_i\)還小,則會發生戰鬥(因為是反比例函式,你求得是最大上界,小於k的都會比\(a_i\) 大,這裡要仔細想一下

因為這樣k具有單調性,比當前界限大的話,lv會變小,則以後戰鬥的怪只會增加不會減少,我們可以二分一個最大的mid, 滿足在這個位置的戰鬥的怪所能到達的等級小於等於\(a_i\), mid - 1位置的戰鬥的怪所能到達的等級都大於\(a_i\), 因為是反函式。

現在我們需要一個資料結構來維護,在一段k的範圍內加 1,這裡記為k能戰鬥的數量(經驗值),詢問某個位置的數量(經驗值),來當作二分得\(check\)函式計算等級, 也就是區間修改和單點查詢,這裡可以用線段樹維護(樹狀陣列也是可以得)。最後二分出的位置記錄下來,當查詢的判斷,修改時修改的是大於等於答案的位置,因為大於等於的位置k越大,lv越小,戰鬥的次數越多。多想一下邊界問題

#include<bits/stdc++.h>

using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<ll,ll>;
using PIII = pair<ll, pair<ll,ll>>;
#define endl "\n"
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define lowbit(x) (x) & (-x)
#define point(x) setiosflags(ios::fixed)<<setprecision(x)
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
struct Info {//一定要初始化
	int val;

	int add = 0;
	Info (int x) {
		val = x;
	}
	Info () {
		val = 0;
	}
};
Info merge(const Info& a, const Info& b) {
	Info c;

	c.val = b.val + c.val;
	
	return c;
}
struct segtree {
	#define ls (u << 1)
	#define rs (u << 1 | 1)
	int n;
	segtree(int n) {init(n);};
	vector<Info> info;
	vector<int> a;
	void init(int n) {
		this->n = n;
		info.resize(n << 2);
		a.resize(n << 1);
	}
	void push_up(int u) {
		info[u] = merge(info[ls], info[rs]);
	}
	void build(int u, int l, int r)
    {
        if(l == r)
        {
            info[u] = Info();//填值
            return ;
        }
        int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		push_up(u);
    }
	void settag(int u, int k) {//處理資料
		info[u].val += k;
		info[u].add += k;
 	}
	void push_down(int u)
	{
		if(info[u].add)
        {
            settag(ls, info[u].add);
            settag(rs, info[u].add);
            info[u].add = 0;
        }
	}
	void update(int u, int l, int r, int pos, int k) {
		if(l == r) {
			info[u] = Info(k);
            return;
		}
        push_down(u);
		int mid = l + r >> 1;
		if(pos <= mid) update(ls, l, mid, pos, k);
		else update(rs, mid + 1, r, pos, k);
		push_up(u);

	};
	void update(int u, int l, int r, int x, int y, int k) {
		if(x <= l && r <= y) {
			settag(u, k);
            return;
		}
        push_down(u);
		int mid = l + r >> 1;
		if(x <= mid) update(ls, l, mid, x, y, k);
		if(mid < y) update(rs, mid + 1, r, x, y, k);
		push_up(u);
	}
	void update(int pos, int v) {
		update(1, 1, n, pos, v);
	}
	void update(int x, int y, int k) {
		update(1, 1, n, x, y, k);
	}
	Info query(int u, int l, int r, int x, int y) {
        if (x <= l && r <= y) return info[u];
        push_down(u);
        int mid = l + r >> 1;
        if (y <= mid) return query(ls, l, mid, x, y);
        else if (mid < x) return query(rs, mid + 1, r, x, y);
        else return merge(query(ls, l, mid, x, y), query(rs, mid + 1, r, x, y));
    }
    Info query(int l, int r) {
        return query(1, 1, n, l, r);
    }

};
void solve(){
	int n, q;
	cin >> n >> q;
	vector<int> a(n + 1), b(n + 1);
	for(int i = 1; i <= n; i ++) cin >> a[i];
	segtree tr(n);//維護區間加,直接lazy標記就行
	for(int i = 1; i <= n; i ++) {
		int l = 1, r = n;
		while(l < r) {
			int mid = l + r >> 1;
			if(tr.query(1, 1, n, mid, mid).val / mid + 1 <= a[i]) r = mid;//判斷等級,單點查詢
			else l = mid + 1;
		}
		b[i] = l;
		tr.update(1, 1, n, l, n, 1);
	}
	while(q --) {
		int x, y; cin >> x >> y;
		if(b[x] <= y) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	
}
int main()
{
	int T=1;
	//cin>>T;
	while(T--){
		solve();
	}
	return 0;
}