P9561 Colorful Segments 题解
赛时一眼秒(假)了,然后花了 5h 写假做法。
思路 5min,代码 2h,调试 2h,赛后才发现假了qwq
赛时唯一贡献是为队友贡献了 20min 的罚时。
\textbf{Solution}
先讲一下我赛时的假思路(因为这对正解很有启发作用):
统计方案数,考虑 dp。
令
很自然的转移方程:
其中
下图是反例(有点丑):
按照我们有的转移方程,
为什么会错呢?因为
那怎么做才对呢?
既然由同色线段转移会出现错误,那只由异色线段转移不就行了?
对于
那么转移方程就是:
二项式定理化一下:
时间复杂度
考虑如何优化。
对于一条线段
我们需要找到
如果我知道
令
那么怎么维护
考虑用一个数据结构维护
显然线段树。由于是单点修改,可以单 Tag。
\textbf{AC Code}
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int n, pow2[N], f[N];
int tr[2][4 * N], mark[2][4 * N]; //0红 1蓝
int ql, qr, d;
struct seg{
int l, r;
bool operator <(const seg &b) const{
return r < b.r;
}
bool operator <(const int &d) const{
return r < d;
}
};
vector<seg> red, blue;
inline int get(int d, int op){
if( !op )
return lower_bound(red.begin(), red.end(), d) - red.begin();
return lower_bound(blue.begin(), blue.end(), d) - blue.begin();
}
inline int MOD(int a, int b){
if( a + b >= mod )
return a + b - mod;
return a + b;
}
inline void pushdown(int l, int r, int p, int op){
if( l != r && mark[op][p] ){
tr[op][p << 1] = tr[op][p << 1] * pow2[mark[op][p]] % mod;
tr[op][p << 1 | 1] = tr[op][p << 1 | 1] * pow2[mark[op][p]] % mod;
mark[op][p << 1] += mark[op][p];
mark[op][p << 1 | 1] += mark[op][p];
mark[op][p] = 0;
}
return;
}
inline void modify(int l, int r, int p, int op, int ope){ //ope0是单点加,ope1是区间乘
if( ql <= l && r <= qr ){
if( !ope )
tr[op][p] = d;
else{
tr[op][p] = MOD(tr[op][p], tr[op][p]);
mark[op][p]++;
}
return;
}
pushdown(l, r, p, op);
int mid = (l + r) >> 1;
if( ql <= mid )
modify(l, mid, p << 1, op, ope);
if( mid < qr )
modify(mid + 1, r, p << 1 | 1, op, ope);
tr[op][p] = MOD(tr[op][p << 1], tr[op][p << 1 | 1]);
return;
}
inline int query(int l, int r, int p, int op){
if( ql <= l && r <= qr )
return tr[op][p];
pushdown(l, r, p, op);
int mid = (l + r) >> 1, ans = 0;
if( ql <= mid )
ans = MOD(ans, query(l, mid, p << 1, op));
if( mid < qr )
ans = MOD(ans, query(mid + 1, r, p << 1 | 1, op));
tr[op][p] = MOD(tr[op][p << 1], tr[op][p << 1 | 1]);
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
pow2[0] = 1;
for(int i = 1; i < N; i++){
pow2[i] = pow2[i - 1] + pow2[i - 1];
if( pow2[i] >= mod ) pow2[i] -= mod;
}
int T;
cin >> T;
while( T-- ){
for(int i = 1; i <= n; i++)
f[i] = 0;
for(int i = 1; i <= 4 * n; i++)
tr[0][i] = tr[1][i] = mark[0][i] = mark[1][i] = 0;
red.clear(); blue.clear();
cin >> n;
for(int i = 1; i <= n; i++){
seg now;
int c;
cin >> now.l >> now.r >> c;
if( !c ) red.push_back(now);
else blue.push_back(now);
}
sort(red.begin(), red.end());
sort(blue.begin(), blue.end());
d = f[0] = 1;
ql = 0, qr = 0;
modify(0, red.size(), 1, 0, 0);
modify(0, blue.size(), 1, 1, 0);
int p1 = 0, p2 = 0, cnt = 0;
while( p1 < red.size() || p2 < blue.size() ){
++cnt;
if( p1 !=red.size() && (p2 == blue.size() || red[p1].r < blue[p2].r) ){ //选红色线段
ql = 0, qr = get(red[p1].l, 1);
d = f[cnt] = query(0, blue.size(), 1, 1);
modify(0, blue.size(), 1, 1, 1);
ql = qr = p1 + 1;
modify(0, red.size(), 1, 0, 0);
p1++;
}
else{
ql = 0, qr = get(blue[p2].l, 0);
d = f[cnt] = query(0, red.size(), 1, 0);
modify(0, red.size(), 1, 0, 1);
ql = qr = p2 + 1;
modify(0, blue.size(), 1, 1, 0);
p2++;
}
}
int ans = 0;
for(int i = 0; i <= n; i++)
ans = MOD(ans, f[i]);
cout << ans << "\n";
}
return 0;
}
LaTeX:@Matrix_mlt