[COCI 2024/2025 #5] Crtež / Luogu P11754 题解

P11754 [COCI 2024/2025 #5] 绘图 / Crtež

解法说明

题面复杂程度可以说是非常诈骗。本题代码难度不高,难度主要在理解题面。

无论如何操作,初始序列都是由 \(-1\) 间隔开的 \(0\) 的连续段。

根据操作说明,我们可以知道两个 \(0\) 的连续段不会互相影响,并且要求的是互不等价的最终序列的方案数,所以根据乘法原理,总方案数就是各个连续段方案数之积。

但是这个也很难求,手模一下找规律。设连续段长度为 \(l\)

\(l=1\) 时,有 \(3\) 种方案:\([0],[-1],[1]\)

\(l=2\) 时,有 \(9\) 种方案:\([-1,-1],[-1,0],[-1,1],[0,-1],[0,0],[1,-1],[1,0],[1,1],[1,2]\)

聪明的你一定已经发现了规律并通过第三个样例验证了该规律:长为 \(l\) 的连续段的方案数为 \(3^l\)

当然还有另一种找规律方法:dp

\(dp_{i,0/1/2/3}\) 表示第 \(i\) 位分别为 \(-1\)\(0\),新数字,上一位填的数字的方案数。

则可以得到转移方程:

\[\begin{cases} dp_{i,0}=dp_{i-1,0}+dp_{i-1,1}+dp_{i-1,2}+dp_{i-1,3}\\ dp_{i,1}=dp_{i-1,0}+dp_{i-1,1}+dp_{i-1,2}+dp_{i-1,3}\\ dp_{i,2}=dp_{i-1,0}+dp_{i-1,2}+dp_{i-1,3}\\ dp_{i,3}=dp_{i-1,2}+dp_{i-1,3} \end{cases} \]

总方案数为四情况之和,容易发现总方案数都是 \(3^i\)

综上,最终解法即为维护当前 \(0\) 的数量 \(t\) 并输出 \(3^t\) 即可,此处使用动态开点线段树,空间卡的比较死。

代码

dp 代码

Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int dp[N][4];
/*
0 -> -1
1 -> 0
2 -> new number
3 -> same number with i-1
*/
signed main(){
    dp[0][0]=1;
    int n=N-10;
    for(int i=1;i<=n;i++){
        dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+dp[i-1][3];
        dp[i][1]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+dp[i-1][3];
        dp[i][2]=dp[i-1][0]+dp[i-1][2]+dp[i-1][3];
        dp[i][3]=dp[i-1][2]+dp[i-1][3];
        cout<<dp[i][1]+dp[i][2]+dp[i][3]+dp[i][0]<<'\n';
    }
    return 0;
} 

AC 代码

Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define int long long
#define mid ((l+r)>>1)
const int N=1e6+10,qq=1e5+10,mod=1e9+7;
int n,Q;
il int qpow(int x,int y){
    int res=1;
    while(y){
        if(y&1)res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
// 动态开点线段树
struct Segment_Tree{
    int t[qq<<8];
    signed ls[qq<<8],rs[qq<<8];
    bool lz[qq<<8];
    int cnt=1;
    il void pu(int nw){
        t[nw]=t[ls[nw]]+t[rs[nw]];
    }
    il void pd(int l,int r,int nw){
        if(lz[nw]){
            if(!ls[nw])ls[nw]=++cnt;
            if(!rs[nw])rs[nw]=++cnt;
            t[ls[nw]]=(mid-l+1)-t[ls[nw]];
            t[rs[nw]]=(r-mid)-t[rs[nw]];
            lz[ls[nw]]^=1,
            lz[rs[nw]]^=1;
            lz[nw]=0;
        }
    }
    void modify(int l,int r,int S,int T,int nw){
        if(S<=l && r<=T){
            t[nw]=(r-l+1)-t[nw];
            lz[nw]^=1;
            return;
        }
        pd(l,r,nw);
        if(mid>=S){
            if(!ls[nw])ls[nw]=++cnt;
            modify(l,mid,S,T,ls[nw]);
        }
        if(mid<T){
            if(!rs[nw])rs[nw]=++cnt;
            modify(mid+1,r,S,T,rs[nw]);
        }
        pu(nw);
    }
}t;
//求总和直接t.t[1]即可。
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(nullptr);
    cin>>n>>Q;
    while(Q--){
        int l,r;
        cin>>l>>r;
        t.modify(1,n,l,r,1);
        cout<<qpow(3,n-t.t[1])<<'\n';
    }
    return 0;
} 
posted @ 2025-03-08 10:40  zxdjmq  阅读(38)  评论(0)    收藏  举报