[40](CSP 集訓)CSP 聯訓模擬 2

HaneDaniko發表於2024-10-05

A.擠壓

經典二進位制拆位

好像也不是那麼經典,CL-22 有提到

那麼這個題因為維護的不是貢獻和而是貢獻平方和,所以考慮怎麼計算

假設我們得到了一個異或後的答案 \(x\),不動腦子的寫成 \(x=(k_12^{1}+k_22^{2}\cdots)^2\),二項式定理可以拆開,變成 \(\sum_{i\operatorname{and}j}2^{i}\times 2^{j}\) (當 \(i\) 有值時,其貢獻為 \(2^i\)\(j\) 同理,拆分後貢獻為二者相乘,可以發現,這兩者中一旦有一個為 \(0\),則其貢獻也為 \(0\),乘起來也就是 \(0\),所以不做統計,關於如何判斷 \(i,j\) 有值,CL-22 裡有說,就是先拆位開桶,然後如果是奇數就有值,偶數就沒有),發現是隻有兩項的形式,所以直接列舉即可

UPD: 碼的誰問的,顯然這裡不是列舉 \(\sum_{i\operatorname{and}j}2^{i}\times 2^{j}\) 再相加,你好歹得套個期望吧

\[E(s^2(x))=\sum_{i,j}E(x_i=1)\times E(x_j=1)\times 2^{i}\times 2^{j} \]

\(f_{i,j,k,l,m}\) 表示考慮到第 \(i\) 位數字,考慮其 \(j,k\) 兩位,\(l,m\in\{0,1\}\) 表示其有值/無值(其實也就是出現的 \(1\) 的個數是奇數還是偶數)的機率,注意這裡求的機率是關於 “從前 \(i\) 個裡選出若干個,最後的結果的第 \(i,j\) 位實現上述狀態的機率”,然後你從 \(i\) 轉移到 \(i+1\) 的時候,直接去判斷當前選還是不選,如果選的話,判斷一下新加入的值的第 \(i,j\) 位是否有值,有的話就將對應奇偶性取反

最後統計的時候直接從 \(i=n,l=1,m=1\) 的狀態里加和即可,要注意乘以 \(2^i\times 2^j\),統計的時候要注意 \(f_{n,j,j,1,1}\) 這樣的結果也要算進去,因為平方拆開是有自平方項的

提前預處理逆元和真實機率,否則跑的賊慢

跟 int_R 學到了炫酷寫法

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
int n;
int a[100001],p0[100001];
int f[2][33][33][2][2];
int power(int a,int t){
    int base=a,ans=1;
    while(t){
        if(t&1){
            ans=ans*base%p;
        }
        base=base*base%p;
        t>>=1;
    }
    return ans;
}
const int inv=power(1000000000,p-2);
signed main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=n;++i){
        scanf("%lld",&p0[i]);
        p0[i]=p0[i]*inv%p;
    }
    for(int i=0;i<=32;++i){
        for(int j=0;j<=32;++j){
            f[0][i][j][0][0]=1;
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=0;j<=32;++j){
            for(int k=0;k<=32;++k){
                for(int l:{0,1}){
                    for(int m:{0,1}){
                        f[i&1][j][k][l][m]=(f[1-(i&1)][j][k][l][m]*(1-p0[i])+f[1-(i&1)][j][k][l^((a[i]>>j)&1)][m^((a[i]>>k)&1)]*p0[i])%p;
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=0;i<=32;++i){
        for(int j=0;j<=32;++j){
            ans+=(1ll<<(i+j))%p*f[n&1][i][j][1][1]%p;
            ans%=p;
        }
    }
    cout<<(ans%p+p)%p;
}

B.工地難題

\(Update\) \(S\infty n\)

C.星空遺蹟

維護一個勝利者棧,在這個棧裡保證前一個元素能贏後一個,只要維護出這個棧,則棧底元素即為勝利者

  • 棧空,插入
  • 棧頂元素輸了或平局,彈出
  • 棧頂元素贏了,插入新元素

這樣做的依據:

  • 兩個可以勝中間元素的元素,將其中間元素清除後結果不變(彈掉失敗者的依據)
  • 一個相同元素的連通塊,縮成一個後不影響結果(彈掉平局者的依據)

轉化成式子大概就是

\[f_i=\begin{cases}f_{i-1}+1&\text{lose}\\f_{i-1}&\text{draw}\\\max(1,f_{i-1}-1)&\text{win}\end{cases} \]

你無視這個對 \(1\)\(\max\),發現在整個過程中的 \(f_i\) 的最小值即為要求的字元所在地

感性理解

所以如果你無視掉這個對 \(1\)\(\max\),那麼我們就可以透過維護原陣列的最小值來求解

然後上線段樹

可以維護一個原陣列的差分陣列,這個差分陣列只有 \(1,0,-1\) 三種取值,然後我們對查分陣列的字首和維護一顆線段樹,因為我們單點修改時,相當於對差分陣列做出最多一項修改,在字首和上體現就是將 \([i,n]\) 的所有值整體平移,那麼我們就需要一個支援區間修改,查詢最小值所在位置的線段樹,查詢位置也很簡單,你只需要在樹節點裡記錄一下 \(pos\) 資訊

然後你做修改的時候(比如把差分陣列 \(dx_i\) 變成 \(dx_i'\)),直接對 \([i,n]\) 增加一個 \(dx_i'-dx_i\) 就行了,非常好寫

然後需要注意的就是,如果遇到兩個都是最小值,那麼應該取下標靠後的那個

#include<bits/stdc++.h>
using namespace std;
int n,q;
inline int ton(char c){
    if(c=='R') return 0;
    if(c=='P') return 2;
    return 1;
}
inline char toc(int i){
    if(i==0) return 'R';
    if(i==2) return 'P';
    return 'S';
}
inline bool win2(int x,int y){
    if(x==0 and y==1) return true;
    if(x==1 and y==2) return true;
    if(x==2 and y==0) return true;
    return false;
}
inline int win(int s,int x){
    return win2(s,x);
    // -1 front win
    // if(s==x) return 0;
    // if(s==1 and x==2) return 1;
    // if(s==1 and x==3) return -1;
    // if(s==2 and x==3) return 1;
    // if(s==2 and x==1) return -1;
    // if(s==3 and x==1) return 1;
    // if(s==3 and x==2) return -1;
}
const int inf=0x3f3f3f3f;
int sum[200001];
namespace stree{
    struct tree{
        int l,r;
        int val,pos;
        int lazy;
    }t[800001];
    #define tol (id*2)
    #define tor (id*2+1)
    #define mid(l,r) mid=((l)+(r))/2
    void pushup(int id){
        if(t[tol].val<=t[tor].val){
            t[id].val=t[tol].val;
            t[id].pos=t[tol].pos;
        }
        else{
            t[id].val=t[tor].val;
            t[id].pos=t[tor].pos;
        }
    }
    void build(int id,int l,int r){
        t[id].l=l;t[id].r=r;
        if(l==r){
            t[id].val=sum[l];
            t[id].pos=l;
            return;
        }
        int mid(l,r);
        build(tol,l,mid);
        build(tor,mid+1,r);
        pushup(id);
    }
    void pushdown(int id){
        if(t[id].lazy){
            t[tol].val+=t[id].lazy;
            t[tol].lazy+=t[id].lazy;
            t[tor].val+=t[id].lazy;
            t[tor].lazy+=t[id].lazy;
            t[id].lazy=0;
        }
    }
    void change(int id,int l,int r,int val){
        // cout<<"change "<<id<<" "<<l<<" "<<r<<" "<<val<<endl;
        if(l<=t[id].l and t[id].r<=r){
            t[id].val+=val;
            t[id].lazy+=val;
            return;
        }
        pushdown(id);
        int mid(t[id].l,t[id].r);
        if(mid>=l) change(tol,l,r,val);
        if(mid<r) change(tor,l,r,val);
        pushup(id);
    }
    tree ask(int id,int l,int r){
        if(l<=t[id].l and t[id].r<=r) return t[id];
        pushdown(id);
        int mid(t[id].l,t[id].r);
        tree res={0,0,inf,0,0};
        if(mid>=l) res=ask(tol,l,r);
        if(mid<r){
            if(res.val>=inf) res=ask(tor,l,r);
            else{
                tree res2=ask(tor,l,r);
                if(res2.val<res.val) res=res2;
            }
        }
        return res;
    }
}
int a[200001];
stack<int>st;
int solve(){
    // for(int i=1;i<=n;++i){
    //     cout<<a[i]<<" ";
    // }
    // cout<<endl;
    while(!st.empty()) st.pop();
    int lastans=0;
    for(int i=1;i<=n;++i){
        while(!st.empty() and win2(st.top(),a[i])==false) st.pop();
        st.push(a[i]);
        if(st.size()==1) lastans=st.top();
    }
    return lastans;
}
int d[200001];
int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d %d",&n,&q);getchar();
    for(int i=1;i<=n;++i){
        a[i]=ton(getchar());
    }
    d[1]=1;
    for(int i=2;i<=n;++i){
        if(win(a[i-1],a[i])) d[i]=1;
        else if(win(a[i],a[i-1])) d[i]=-1;
        else d[i]=0;
    }
    for(int i=1;i<=n;++i){
        // cout<<d[i]<<" ";
        sum[i]=sum[i-1]+d[i];
    }
    // cout<<endl;
    stree::build(1,1,n);
    while(q--){
        int op;scanf("%d",&op);
        int k,l,r;char x;
        if(op==1){
            scanf("%d %c",&k,&x);
            a[k]=ton(x);
            int bef,aft,now=ton(x);
            if(k!=1){
                bef=d[k];aft=0;
                if(win(a[k-1],now)) aft=1;
                if(win(now,a[k-1])) aft=-1;
                d[k]=aft;
                stree::change(1,k,n,aft-bef);
                // cout<<aft<<" "<<bef<<" 1"<<aft-bef<<endl;
            }
            if(k!=n){
                bef=d[k+1];aft=0;
                if(win(now,a[k+1])) aft=1;
                if(win(a[k+1],now)) aft=-1;
                d[k+1]=aft;
                stree::change(1,k+1,n,aft-bef);
                // cout<<aft<<" "<<bef<<" 2"<<aft-bef<<endl;
            }
            a[k]=now;
        }
        else{
            scanf("%d %d",&l,&r);
            printf("%c\n",toc(a[stree::ask(1,l,r).pos]));
        }
    }
}

相關文章