P11498.
纪念一下 2025 年第一题。
发现 \(a\) 中后面的数必须是前面的数的超集,不难想到拆位,先考虑 \(k=2^p\) 的情况。
对于一个数位而言,一定是覆盖 \(a\) 的一段后缀(可以为空),设该段后缀为 \(x\sim n\),称 \(x\) 为该数位的起始点。\(a_{l_i}\neq a_{r_i}\) 的限制实际上就是要求 \(l_i+1\sim r_i\) 中存在一个起始点,因此下文默认将题目给出的 \(l_i\) 加上 \(1\)。
套路地,设计 dp 状态 \(f_{i,j}\) 代表从前往后的第 \(j\) 个起始点放在 \(i\),所有左端点 \(\le i\) 的限制都已经被满足的方案数。转移考虑上一个起始点的位置,设 \(x\) 为所有满足 \(r_i<i\) 的限制中 \(l_i\) 最大是多少,那么上一个起始点必须放在 \(x\sim i\),用前缀和优化做到 \(O(1)\) 转移。
上面所说的 dp 的总复杂度是 \(O(n\log k)\)。考虑当 \(k=2^p\) 时如何统计答案,显然我们可以任选 \(0\sim p\) 个数位填上去。枚举填了 \(j\) 个数位,同时枚举最后一个起始点放在 \(i\)(\(i\) 需要大于等于所有限制的左端点),此时所有数位无标号的答案是 \(f_{i,j}\)。但是实际上是有标号的,我们不妨钦定 dp 时不能有数位的起始点重合,然后枚举有 \(x\) 个位置不同的起始点,此时的问题等价于将 \(j\) 个有标号的球放进 \(x\) 个有标号的集合里的方案数(每个集合不能为空),这个东西可以预处理出来(容斥或者递推),设为 \(h_{j,x}\),乘上前面的 dp 值得到 \(h_{j,x}f_{i,x}\)。统计答案的这一步是 \(O(\log^2 k)\) 的。
考虑 \(k\ne 2^p\) 时的做法。采用数位 dp 的经典套路,钦定 \(k\) 所有为 \(1\) 的数位中,\(i\) 是第一个没被选中的(顺序为从高位到低位)。此时可以选的数位的数量形如 \(num\sim num+i\),我们枚举选了多少个,做刚才的统计答案,可以做到 \(O(n\log^3 k)\),常数巨大。
这个复杂度还是太菜了。我们需要枚举 \(k\) 的所有数位,同时需要枚举选了多少个,还需要枚举有多少个位置不同的起始点。考虑预处理 \(g_i\) 代表选了 \(i\) 个数位的方案数。此时可以做到 \(O(\log^2 k)\) 统计答案(只需要枚举前两个量)。
\(g_i\) 的预处理可以简单做到 \(O(\log^2 k)\)。注意 dp 时需要先枚举第二维才能维护前缀和。总时间复杂度 \(O(n\log k+\log^2 k)\)。
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii std::pair<int,int>
#define vint std::vector<int>
#define vpair std::vector<pii>
#define debug(...) fprintf(stderr,##__VA_ARGS__)template<typename T>
void read(T &x){x=0;int f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();x*=f;
}std::stack<char>st;
template<typename T>
void print(T x){if(x==0) putchar('0');if(x<0) putchar('-'),x=-x;while(st.size()) st.pop();while(x) st.push((char)('0'+x%10)),x/=10;while(st.size()) putchar(st.top()),st.pop();
}template<typename T>
void printsp(T x){print(x),putchar(' ');
}template<typename T>
void println(T x){print(x),putchar('\n');
}template<typename T,typename I>
bool chkmin(T &a,I b){if(a>b) return a=b,1;return 0;
}template<typename T,typename I>
bool chkmax(T &a,I b){if(a<b) return a=b,1;return 0;
}template<typename T,typename I>
void addedge(std::vector<I>*vec,T u,T v){vec[u].push_back(v);
}template<typename T,typename I,typename K>
void addedge(std::vector<K>*vec,T u,T v,I w){vec[u].push_back({v,w});
}template<typename T,typename I>
void addd(std::vector<I>*vec,T u,T v){addedge(vec,u,v),addedge(vec,v,u);
}template<typename T,typename I,typename K>
void addd(std::vector<K>*vec,T u,T v,I w){addedge(vec,u,v,w),addedge(vec,v,u,w);
}bool Mbe;const int inf=1e18,MOD1=998244353,MOD2=1e9+7;const int maxn=3e5+10;int n,m,k,f[maxn][70],c[70];int lowbit(int x){return x&(-x);
}int fac[maxn],g[70],gt[70][70],ifac[70],xm[maxn],sum[maxn];void add(int &a,int b){a+=b;if(a>=MOD2) a-=MOD2;
}int ksm(int x,int y,int p){if(y==0) return 1;int z=ksm(x,y>>1,p);z=z*z%p;if(y&1) z=z*x%p;return z;
}int C(int x,int y){if(y<0||x<y) return 0;return fac[x]*ifac[y]%MOD2*ifac[x-y]%MOD2;
}void init(){fac[0]=1;for(int i=1;i<=maxn-10;i++) fac[i]=fac[i-1]*i%MOD2;ifac[69]=ksm(fac[69],MOD2-2,MOD2);for(int i=68;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%MOD2;for(int i=0;i<=65;i++)for(int j=0;j<=i;j++){gt[i][j]=ksm(j,i,MOD2);for(int k=0;k<j;k++) add(gt[i][j],(MOD2-gt[i][k]*C(j,k)%MOD2));}
}int qzh[maxn];pii a[maxn];void psolve(int x){f[0][0]=1;qzh[0]=1;for(int i=1;i<=n;i++) qzh[i]=qzh[i-1];int b;if(m==0) b=1;else b=a[m].fi;for(int i=1;i<=x;i++){for(int j=1;j<=n;j++){f[j][i]=qzh[j-1];if(xm[j]!=-inf) add(f[j][i],MOD2-qzh[xm[j]-1]);}qzh[0]=0;for(int j=1;j<=n;j++) qzh[j]=qzh[j-1],add(qzh[j],f[j][i]);sum[i]=qzh[n],add(sum[i],MOD2-qzh[b-1]);}int res=0;g[0]=(m==0);for(int i=1;i<=x;i++)for(int j=1;j<=i;j++)add(g[i],sum[j]*gt[i][j]%MOD2);
}bool Men;signed main(){debug("%.6lfMB\n",(&Mbe-&Men)/1048576.0);read(n),read(m),read(k);for(int i=1;i<=m;i++) read(a[i].fi),read(a[i].se),a[i].fi++;std::sort(a+1,a+m+1);for(int i=0;i<=m;i++) xm[i]=-inf;for(int i=1;i<=m;i++) chkmax(xm[a[i].se+1],a[i].fi);for(int i=1;i<=n;i++) xm[i]=std::max(xm[i],xm[i-1]);init();psolve(65);int cnt=-1;if(k==0) c[++cnt]=0;while(k) c[++cnt]=(k&1),k>>=1;int num=0,ans=0;for(int i=cnt;i>=0;i--){if(c[i]!=1) continue;for(int j=num;j<=num+i;j++) add(ans,g[j]*C(i,j-num)%MOD2);num++;}add(ans,g[num]);println(ans);debug("%.6lfms\n",1e3*clock()/CLOCKS_PER_SEC);
}