[luogu5358] [SDOI2019R2 d1t1] 快速查詢 - O(1)數據結構 - 線性求逆元

傳送門:https://www.luogu.org/problemnew/show/P5358

題目大意:給定初始全0的序列,支持:單點修改,全局加,全局乘,全局賦值,單點查詢,全局查詢。要求每次操作O(1)。

現場平衡樹過了的神仙深受我一拜。

注意到操作只有單點和全局,而且雖然序列很長但是修改的點最多1e5個,我們就可以把所有的點拎出來重標號。

我們發現,其實最麻煩的地方是怎麼查詢單點的值,其餘直接模擬就好啦。

需要維護的東西有:一個“標準元素”(未被單點修改的元素)的值,全局所有元素的和。

還需要記錄一下一個點上一次被單點修改是什麼時候以及上一次全局賦值是什麼時候。

單點查詢時,如果上一次全局賦值在它上一次單點修改之後,那麼它的值當然是“標準元素”啦。

否則呢?考慮從上一次單點修改之後,這個元素經歷了什麼:

設x爲上一次單點修改時的初值:

((x×a1+b1)×a2+b2)×a3+b3......

這其中x的貢獻爲:

x×a1×a2×a3×……

如果在上一次單點修改時,標準元素的值是y,則兩者最終相差:

(x-y)×a1×a2×a3×……

因此我們只需要記錄下修改時(x-y)的值,再想辦法求出這段區間內所有3操作的連乘即可求出答案。

區間連乘怎麼求?可以看作是一個前綴乘一個前綴的逆元。

對每個前綴求逆元,只需要像預處理階乘的逆元那樣,求出全局乘積的逆元再倒着算回去。

然而這就做完了嗎?你會發現你獲得了0分的好成績。

原因是你可能會除以0。

因此你還需要記錄區間內有多少個0,並在求前綴積時把所有爲0的項忽略掉。查詢時先判斷區間內有沒有0,沒有的話再算前綴乘前綴的逆元就是對的了。

#include<bits/stdc++.h>
using namespace std;
#define gc getchar()
#define pc putchar
#define li long long
inline li read(){
    li x = 0,y = 0,c = gc;
    while(!isdigit(c)) y = c,c = gc;
    while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ '0'),c = gc;
    return y == '-' ? -x : x;
}
inline void print(li q){
    if(q < 0) pc('-'),q = -q;
    if(q >= 10) print(q / 10);
    pc(q % 10 + '0');
}
const int mo = 10000019;
inline li ksm(li q,li w){
    li as = 1;
    while(w){
        if(w & 1) as = as * q % mo;
        q = q * q % mo;
        w >>= 1;
    }
    return as;
}
int n,m,t;
struct qy{
    int op,x,y;
}a[100010];
unordered_map<int,int> mp;
li qa[110],qb[110];
int bh[10000010],nw,tot;
int jc[10000010],nj[10000010],ling[10000010];
int lst[100010];
li vl[100010];
li an;
int main(){
    int i,j,u,v;
    n = read();m = read();
    for(i = 1;i <= m;++i){
        a[i].op = read();
        if(a[i].op == 1){
            a[i].x = read();
            if(!mp[a[i].x]) mp[a[i].x] = ++nw;
            a[i].x = mp[a[i].x];
            a[i].y = (read() % mo + mo) % mo;
        }
        else if(a[i].op <= 4){
            a[i].x = (read() % mo + mo) % mo;
        }
        else if(a[i].op == 5){
            a[i].x = read();
            if(!mp[a[i].x]) mp[a[i].x] = ++nw;
            a[i].x = mp[a[i].x];
        }
    }
    t = read();tot = m * t;
    for(i = 1;i <= t;++i){
        qa[i] = read();qb[i] = read();
    }
    for(i = 1;i <= t;++i) for(j = 1;j <= m;++j) bh[(i - 1) * m + j] = (qa[i] + j * qb[i]) % m + 1;
    jc[0] = 1;
    for(i = 1;i <= tot;++i){
        if(a[bh[i]].op != 3) jc[i] = jc[i - 1],ling[i] = ling[i - 1];
        else if(a[bh[i]].x) jc[i] = 1ll * jc[i - 1] * a[bh[i]].x % mo,ling[i] = ling[i - 1];
        else jc[i] = jc[i - 1],ling[i] = ling[i - 1] + 1; 
    }
    nj[tot] = ksm(jc[tot],mo - 2);
    for(i = tot - 1;i >= 0;--i){
        if(a[bh[i + 1]].op == 3 && a[bh[i + 1]].x) nj[i] = 1ll * nj[i + 1] * a[bh[i + 1]].x % mo;
        else nj[i] = nj[i + 1];
    }
    li nw1 = 0,nw2 = 0,val,tmp;
    int lt = 0;
    for(i = 1;i <= tot;++i){
        if(a[bh[i]].op == 1){
            u = a[bh[i]].x;
            if(lt >= lst[u]) val = nw1;
            else{
                v = lst[u];
                if(ling[i] - ling[v]) tmp = 0;
                else tmp = 1ll * jc[i] * nj[v] % mo;
                val = (nw1 + vl[u] * tmp) % mo;
            }
            (nw2 += a[bh[i]].y - val + mo) %= mo;
            vl[u] = (a[bh[i]].y - nw1 + mo) % mo;
            lst[u] = i;
        }
        else if(a[bh[i]].op == 2){
            (nw1 += a[bh[i]].x) %= mo;
            (nw2 += 1ll * n * a[bh[i]].x) %= mo;
        }
        else if(a[bh[i]].op == 3){
            (nw1 *= a[bh[i]].x) %= mo;
            (nw2 *= a[bh[i]].x) %= mo;
        }
        else if(a[bh[i]].op == 4){
            nw1 = a[bh[i]].x;
            nw2 = 1ll * n * a[bh[i]].x % mo;
            lt = i;
        }
        else if(a[bh[i]].op == 5){
            u = a[bh[i]].x;
            if(lt >= lst[u]) val = nw1;
            else{
                v = lst[u];
                if(ling[i] - ling[v]) tmp = 0;
                else tmp = 1ll * jc[i] * nj[v] % mo;
                val = (nw1 + vl[u] * tmp) % mo;
            }
            (an += val) %= mo;

        }
        else{
            (an += nw2) %= mo;
        }
    }
    print(an);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章