P9221 题解
P9221 题解
题面
思路
\(dp_{i,j}\) 表示第 \(i\) 行,第 \(j\) 列的方案数。
则有转移式 \(\begin{aligned}dp_{i,j}=\sum_{lst<k<nxt}dp_{i-1,k}\end{aligned}\)
\(lst,nxt\) 表示在第 \(i\) 行中,第 \(j\) 列上一个和下一个不能经过的纵坐标。(如果没有就是 \(0\) 或 \(m\)。)
但是注意到以下两个点。
- \(n,m\leqslant10^9\),直接 dp 显然是不行的,那这题就不是 dp 吗?答案显然是“不是,它就是 dp。”而 \(q\) 的范围只有 \(10^5\),换句话说有不能经过的点的行数最多只有 \(q\)。
- 假设第 \(i,i+1\) 行都没有任何不能经过的点,那么按照前面的式子,则有 \(\begin{aligned}dp_{i,j}=\sum_{k=1}^m dp_{i-1,k}\end{aligned}\),所以就有 \(\begin{aligned}\sum_{j=1}^mdp_{i,j}=m\sum_{k=1}^mdp_{i-1,k}\end{aligned}\),再者就有 \(\begin{aligned}dp_{i+1,j}=\sum_{j=1}^mdp_{i,j}=m\sum_{k=1}^mdp_{i-1,k}\end{aligned}\)。
两点结合一下,事实上真正需要转移的最多只有 \(q\) 行,其他的可以用快速幂解决,这样的复杂度就变成 \(O(q(q+\log m))\)。
但是这样也是不行的,我们观察转移式,在一次转移式中,发现 \(lst\sim nxt\) 中的每一个点的值相同,所以相当于区间赋值,并且单点(不能经过的点)赋值为 \(0\)。
那就可以用线段树优化了,线段树里统计的就是上一行的 dp 数值,时间复杂度就变成 \(O(q(\log q+\log m))\) 了,非常好,但是空间复杂度炸了,变成 \(m\log m\)。
不急,还有一计,我们可以用动态开点线段树,这样一次赋值就只会改变 \(\log m\) 个区间,所以空间复杂度就优化成 \(O(q\log m)\)
那么问题就解决了,代码细节比较多,记得仔细处理边界情况。(每一行的第一个不能经过的点的前面和最后一个不能经过的点的后面的情况还有第一行之前和最后一行之后的情况。)
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
//#define gc getchar
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#define ll long long
using namespace std;
const int MN=1e5+5,mod=998244353;
ll n,m,q,rt,idx,x[MN],y[MN];
char buf[1<<23],*p1=buf,*p2=buf;
void write(ll n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
ll read(){ll x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}return x*f;}
ll ksm(ll a, ll b){ll res=1;while(b){if(b&1)res=res*a%mod;a=a*a%mod;b>>=1;}return res;}
struct sgt{
struct tree{ll ls,rs,num,tag;}t[MN<<5];
void newtree(ll &p){t[p=++idx]={0,0,0,-1};}
void update(ll p){t[p].num=(t[t[p].ls].num+t[t[p].rs].num)%mod;}
void pushdown(ll p, ll l, ll r){
ll mid=l+r>>1;
if(t[p].tag!=-1){
if(!t[p].ls) newtree(t[p].ls);
if(!t[p].rs) newtree(t[p].rs);
t[t[p].ls].tag=t[t[p].rs].tag=t[p].tag;
t[t[p].ls].num=(mid-l+1)*t[p].tag%mod;
t[t[p].rs].num=(r-mid)*t[p].tag%mod;
t[p].tag=-1;
}
}
void change(ll &p, ll l, ll r, ll L, ll R, ll v){
if(!p) newtree(p);
if(L<=l&&r<=R){t[p].tag=v;t[p].num=v*(r-l+1)%mod;return;}
ll mid=l+r>>1;pushdown(p,l,r);
if(L<=mid) change(t[p].ls,l,mid,L,R,v);
if(R>mid) change(t[p].rs,mid+1,r,L,R,v);
update(p);
}
ll query(ll &p, ll l, ll r, ll L, ll R){
if(!p) newtree(p);
if(L<=l&&r<=R) return t[p].num;
ll mid=l+r>>1,res=0;pushdown(p,l,r);
if(L<=mid) res=(res+query(t[p].ls,l,mid,L,R))%mod;
if(R>mid) res=(res+query(t[p].rs,mid+1,r,L,R))%mod;
return res;
}
}t;
int main(){
//ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
n=read();m=read();q=read();
for(int i=1; i<=q; i++) x[i]=read(),y[i]=read();
if(x[1]) t.change(rt,1,m,1,m,ksm(m,x[1]-1));
else t.change(rt,1,m,1,m,1);
for(int i=1; i<=q; i++){
if(i==1||x[i]==x[i-1]){
t.change(rt,1,m,y[i],y[i],0);
if(y[i-1]+1<=y[i]-1) t.change(rt,1,m,y[i-1]+1,y[i]-1,t.query(rt,1,m,y[i-1]+1,y[i]-1));
}
else if(i!=1){
if(y[i-1]+1<=m) t.change(rt,1,m,y[i-1]+1,m,t.query(rt,1,m,y[i-1]+1,m));
if(x[i]-x[i-1]-1) t.change(rt,1,m,1,m,t.query(rt,1,m,1,m)*ksm(m,x[i]-x[i-1]-2)%mod);
t.change(rt,1,m,y[i],y[i],0);
if(1<=y[i]-1) t.change(rt,1,m,1,y[i]-1,t.query(rt,1,m,1,y[i]-1));
}
}
if(q&&y[q]+1<=m) t.change(rt,1,m,y[q]+1,m,t.query(rt,1,m,y[q]+1,m));
if(n-x[q]) t.change(rt,1,m,1,m,t.query(rt,1,m,1,m)*ksm(m,n-x[q]-1)%mod);
write(t.query(rt,1,m,1,m));putchar('\n');
return 0;
}//250802