[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;
} 
浙公网安备 33010602011771号