冲刺国赛模拟 36

发布时间 2023-07-14 17:28:08作者: gtm1514

感觉思想都不难,但是场上不是想不出来就是写不动,见了鬼了。

染色题

考虑一个转化:对于奇数位置,在 \(a_i\neq a_{i+2}\) 的地方放一个红球,对于偶数位置在同样的地方放个蓝球。这样每个区间的限制变成 \([l,r-2]\) 不能同时有红蓝球,转移前缀和优化一下即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
const int mod=998244353;
int n,m,dp[1000010],sum1[1000010],sum2[1000010],a[1000010];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)a[i]=i;
    for(int i=1;i<=m;i++){
        int l,r;scanf("%d%d",&l,&r);
        if(r>=2)a[r-2]=min(a[r-2],l);
    }
    for(int i=n-1;i>=1;i--)a[i]=min(a[i],a[i+1]);
    dp[0]=sum2[0]=1;
    for(int i=1;i<=n;i++){
        if(i&1){
            dp[i]=(sum1[i-1]+sum2[a[i]-1])%mod;
            sum1[i]=(sum1[i-1]+dp[i])%mod;
            sum2[i]=sum2[i-1];
        }
        else{
            dp[i]=(sum2[i-1]+sum1[a[i]-1])%mod;
            sum1[i]=sum1[i-1];
            sum2[i]=(sum2[i-1]+dp[i])%mod;
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++)ans=(ans+dp[i])%mod;
    ans=2ll*ans%mod;
    printf("%d\n",ans);
}

石头剪刀布

先考虑标准淘汰赛的过程怎么处理:设 \(f_{i,j}\)\([i,i+2^j]\) 的胜者,转移容易倍增合并。

然后考虑设 \(g_{i,j}\)\(u\in[i,i+2^j-1]\)\([i,i+2^j-1]\) 的胜者,转移可以根据 \(f\) 得到。

现在考虑回答询问。容易发现一个区间可以拆成 \(\log\)\(g\),剩下的都是 \(f\)。可以类似线段树地分治递归处理。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <unordered_map>
using namespace std;
const int mod=998244353,inv3=(mod+1)/3;
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
int n,m,trans[3][3],a[300010];
char s[300010];
struct node{
    int a[3];
    node(){memset(a,0,sizeof(a));}
    node operator+(const node&s)const{
        node ans;
        for(int i=0;i<=2;i++)ans.a[i]=(a[i]+s.a[i])%mod;
        return ans;
    }
    node operator*(const node&s)const{
        node ans;
        for(int i=0;i<=2;i++){
            for(int j=0;j<=2;j++){
                ans.a[i]=(ans.a[i]+1ll*a[i]*s.a[j]%mod*trans[i][j])%mod;
                if(i==j)continue;
                ans.a[j]=(ans.a[j]+1ll*a[i]*s.a[j]%mod*trans[j][i])%mod;
            }
        }
        return ans;
    }
}val[3],f[300010][20],g[300010][20];
node query(int L,int R,int l,int r){
    if(l<=L&&R<=r)return g[L][__lg(R-L+2)];
    int mid=(L+R)>>1;
    node val;
    if(l<=mid)val=val+f[mid][__lg(R-mid+1)]*query(L,mid-1,l,r);
    if(mid<r)val=val+f[L][__lg(mid-L+1)]*query(mid+1,R,l,r);
    return val;
}
int main(){
    scanf("%d%d%s",&n,&m,s+1);
    for(int i=1;i<=n;i++){
        if(s[i]=='R')a[i]=0;
        if(s[i]=='P')a[i]=1;
        if(s[i]=='S')a[i]=2;
    }
    trans[0][0]=trans[1][1]=trans[2][2]=1;
    trans[0][1]=trans[1][2]=inv3;trans[0][2]=inv3<<1;
    trans[1][0]=trans[2][1]=inv3<<1;trans[2][0]=inv3;
    val[0].a[0]=val[1].a[1]=val[2].a[2]=1;
    for(int i=1;i<=n;i++)f[i][0]=g[i][1]=val[a[i]];
    for(int j=1;j<=__lg(n)+1;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            f[i][j]=f[i][j-1]*f[i+(1<<j-1)][j-1];
        }
    }
    for(int j=2;j<=__lg(n)+1;j++){
        for(int i=1;i+(1<<j)-2<=n;i++){
            g[i][j]=g[i][j-1]*f[i+(1<<j-1)-1][j-1]+f[i][j-1]*g[i+(1<<j-1)][j-1];
        }
    }
    while(m--){
        int l,r,x,y;scanf("%d%d%d%d",&l,&r,&x,&y);
        int ans=query(l,r,x,y).a[0];
        if(x-l&1)x++;
        ans=1ll*ans*qpow((y-x>>1)+1,mod-2)%mod;
        printf("%d\n",ans);
    }
    return 0;
}

树状数组

首先考虑关键性质:减个 lowbit 之后比 lowbit 低的所有位都变成 \(0\),而高位不会被 lowbit 影响。这意味着我们可以找出使得低位全 \(0\) 在跳一段之后仍然为全 \(0\) 的位置,然后只会跳 \(O(k)\) 次。低位可以预处理每个位置以 \(0\) 开始走到结尾的答案,高位可以直接异或后半段。预处理可以从低位到高位扫,然后每一位从后往前处理,根据之前得到的信息一直跳。

但是这题难度基本全在怎么写。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <unordered_map>
using namespace std;
int n,m,k,A,B;
int nxt[500010][31][2],val[500010],a[500010],ans[500010];
int lowbit(int x){
    return x&-x;
}
int query(int l,int r){
    return val[r]^val[l-1];
}
void calc(int d){
    nxt[n+1][d][0]=n+1;
    for(int i=n;i>=1;i--){
        if(a[i]==-1)nxt[i][d][0]=nxt[i][d][1]=i+1;
        else{
            if(nxt[i][d-1][0]){
                if((query(i,nxt[i][d-1][0]-1)>>d)&1){
                    nxt[i][d][0]=nxt[nxt[i][d-1][0]][d][1];
                    nxt[i][d][1]=nxt[i][d-1][0];
                }
                else{
                    nxt[i][d][0]=nxt[i][d-1][0];
                    nxt[i][d][1]=nxt[nxt[i][d-1][0]][d][1];
                }
            }
        }
    }
}
int find(int x){
    const int U=(1<<k)-1;
    for(int i=k-1;i>=0;i--){
        if(nxt[x][i][0]){
            int ss=(1<<i+1)-1;ss=U^ss;
            return (query(x,n)&ss)|(ans[nxt[x][i][0]]&(U^ss));
        }
    }
    return query(x,n);
}
int lastans;
int getans(int pos,int x){
    const int U=(1<<k)-1;
    for(int i=0;i<k;i++){
        if((x>>i)&1){
            if(!nxt[pos][i][1]){
                int ss=(1<<i)-1;ss=U^ss;
                return ((x^query(pos,n))&ss)|(ans[pos]&(U^ss));
            }
            int ss=(1<<i+1)-1;ss=U^ss;
            x^=query(pos,nxt[pos][i][1]-1)&ss;
            x-=lowbit(x);
            pos=nxt[pos][i][1];
        }
    }
    return ans[pos];
}
int main(){
    scanf("%d%d%d%d%d",&n,&m,&k,&A,&B);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        if(a[i]==-1)val[i]=val[i-1];
        else val[i]=val[i-1]^a[i];
    }
    nxt[n+1][0][0]=n+1;
    for(int i=n;i>=1;i--){
        if(a[i]==-1)nxt[i][0][0]=nxt[i][0][1]=i+1;
        else if(a[i]&1){
            nxt[i][0][0]=nxt[i+1][0][1];
            nxt[i][0][1]=i+1;
        }
        else{
            nxt[i][0][0]=i+1;
            nxt[i][0][1]=nxt[i+1][0][1];
        }
    }
    for(int i=1;i<k;i++)calc(i);
    for(int i=n;i>=1;i--)ans[i]=find(i);
    while(m--){
        int l,x;scanf("%d%d",&l,&x);
        l=l^((1ll*A*lastans+B)%n);x=x^((1ll*A*lastans+B)&((1<<k)-1));
        printf("%d\n",lastans=getans(l,x));
    }
    return 0;
}