SS241031B. 三色(color)
SS241031B. 三色(color)
题意
有一排 \(n\) 个格子,你需要在每个格子涂上 \(1/2/3\) 中的一种颜色。有 \(m\) 个限制,每个限制给你 \(l_i,r_i,x_i\),要求 \([l_i,r_i]\) 的颜色数量恰好是 \(x_i\)。问涂颜色的方案数。
\(n\le 5000,m\le 10^6,x_i\in [1,3]\)。
solution
首先我们感觉这是一个 \(O(n^2+m(\log m?))\) 的复杂度。但是 \(n^2\) 的状态不好做。于是考虑另一种思路,先设最暴力的状态,然后再优化。
设 \(f_{i,pos_1,pos_2,pos_3}\) 表示考虑到第 \(i\) 个格子,最后一次出现颜色 \(j\) 在 \(pos_j\) 位置,的合法涂色方案数。状态是 \(O(n^4)\) 的。
转移就枚举当前位置涂什么颜色。
考虑如何处理限制,考虑 \(r=i\) 的所有限制,相当于要求恰有 \(x\) 个颜色的 \(pos \ge l\)。
对于 \(x\) 相同的限制,发现如果 \(l\) 最大的那个限制,我们选择了若干个颜色要求其 \(pos\ge l\),没有被选中的颜色要求其 \(pos<l\),那么对于 \(l'<l\) 的限制,一定只能选择同样的颜色,而且发现实际上要求是选中的颜色 \(pos\ge\) 最大的 \(l\),没有选中的颜色 \(pos<\) 最小的 \(l\)。
所以可以先把全部转移过来,然后枚举 \(x\),然后枚举选择的颜色,然后再反过来把不合法的删掉。
需要前缀和优化一下,这样复杂度就是 \(O(n^4+m)\)。
发现其实 \(pos_{0/1/2}\) 必定有一个 \(=i\),因此删掉一维,变成 \(O(n^3+m)\)。
实际上你并不关心 \(i\) 涂什么颜色,也不关心 \(pos_{0/1}\) 分别表示的是什么颜色,你只关心最后一次出现位置 \(\ge l\) 的颜色种类数量,不关心是哪种颜色。因此这样去掉一维是方便做的。
然后你可以想到滚掉 \(i\) 这一维,并且转移后面两维的时候少修改一些地方,尽量只修改 \(O(n)\) 个位置。这样就可以有 \(O(n^2+m)\) 的复杂度了,空间复杂度等同。
题解做法写得挺好懂的,直接搬上来。
注:\(j,k\) 即上文的 \(pos_{0/1}\)。题解有一个地方打错字了,情况
\(\to\)清空
。
code
参考了 std 的实现。。。感觉实现起来有点绕,思维难度较高。有一点加一减一的细节。
#include<bits/stdc++.h>
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x-y;x>=z;x--)
using namespace std;
typedef long long ll;
#define isdigit(x) (x>='0'&&x<='9')
#define gc getchar_unlocked
template <typename T>
void read(T &x) {
x=0;
char ch=gc();
for(;!isdigit(ch);ch=gc()) ;
for(;isdigit(ch);ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
}
constexpr int N=5e3+7,M=1e6+7,mod=1e9+7;
int add(int a,int b) { return a+b>=mod?a+b-mod:a+b; }
void _add(int &a,int b) { a=add(a,b); }
void _min(int &a,int b) { a=min(a,b); }
void _max(int &a,int b) { a=max(a,b); }
int t,n,m;
int l,r,x;
int lim[N][4];
int f[N][N];
int sum[N];
int it[N][2];
int ans;
void init() {
ans=0;
rep(i,0,n) sum[i]=lim[i][1]=lim[i][3]=it[i][0]=0, lim[i][0]=lim[i][2]=it[i][1]=i;
rep(i,0,n) rep(j,0,n) f[i][j]=0;
}
void update (int i,int l,int r) {
l=min(l,it[i][1]+1);
while(it[i][0]<l) {
int w=f[i][it[i][0]];
_add(sum[i],mod-w), _add(sum[it[i][0]],mod-w), f[i][it[i][0]++]=0;
}
r=max(r,it[i][0]);
while(it[i][1]>=r) {
int w=f[i][it[i][1]];
_add(sum[i],mod-w), _add(sum[it[i][1]],mod-w), f[i][it[i][1]--]=0;
}
}
int main() {
#ifdef LOCAL
freopen("my.out","w",stdout);
#else
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
#endif
read(t);
while(t--) {
read(n),read(m);
init();
rep(i,1,m) {
read(l),read(r),read(x);
switch(x) {
case 1: _min(lim[r][0],l); break;
case 2: _max(lim[r][1],l), _min(lim[r][2],l); break;
case 3: _max(lim[r][3],l); break;
}
}
f[0][0]=3,sum[0]=6;
rep(i,1,n) {
rep(j,0,i-2) f[i-1][j]=sum[j], _add(sum[i-1],sum[j]), _add(sum[j],sum[j]); // 第 i 位涂和第 i-1 位不一样的颜色
rep(j,0,lim[i][1]-1) update(j,0,0);// 小于 lim_1 的全部删除
rep(j,lim[i][1],lim[i][0]-1) update(j,lim[i][3],lim[i][2]); // 在 [lim_1,lim_0) 之间的保留列 [lim_3,lim_2)
rep(j,lim[i][0],i-1) update(j,0,0); //大于等于 lim_0 的全部删除
}
rep(i,0,n) rep(j,0,n) _add(ans,f[i][j]);
pf("%d\n",ans);
}
}
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18517663