P10052 [CCO 2022] Double Attendance Sol
思路
考虑DP。
注意到这个停留的性质近乎无用,因为我们可以停留无限短的时间,因此实际上就自动少一秒就行了。
由于时间放状态里肯定空间爆炸,因此只能设计 \(dp_i\),表示看 \(i\) 张幻灯片需要的时间,那么很自然想到 \(dp_{i,j}\) 表示到看 \(i\) 个幻灯片,并且当前在 \(j\) 教室使用的最少时间,但是这样子转移非常痛苦,因为我们容易发现,我们不知道旧的幻灯片有没有被看过。
因此设计 \(dp_{i,j,k}\) ,其中 \(k\) 表示对面的幻灯片有没有被看过,有的话为1,否则为0。
转移可以看代码,还是比较好想的。
具体展开下:
假定当前处理到了 tim = dp_{i,j,k},当前位置为nw,如果当前不属于任何区间,nw=0,下一个位置为 nxt,另一个房间的区间为oth,则有以下转移(扩展):
f[i + (oth && (k ^ 1))][j^1][(nw && (find(j,tim+2*K) == nw))] = min(f[i + (oth && (k ^ 1))][j^1][(nw && (find(j,tim+2*K) == nw))],tim + K);
这个式子的意思是:考虑从到另一个房间,如果对面房间有幻灯片 oth 并且我没有看过 oth,那么就 +1,否则不变。如果当前的 nw 与在 tim+2K 的时刻看到的是一个幻灯片,那么 k=1,否则为 0 。
f[i+1][j][(k && oth && (find(j^1,a[j][nxt].l + K) == oth))] = min(f[i+1][j][(k && oth && (find(j^1,a[j][nxt].l + K) == oth))],a[j][nxt].l);
这个式子的意思是:考虑留在当前房间,如果在 tim 时刻对面的幻灯片和等到下一个时刻的幻灯片是一个,且当前 k=1,那么就 k=1,否则不变。
所以 DP 的式子是好想的,但是因为细节很多,所以看着是依托。
注意到最多只需要重复一次,因此不需要跑最短路,转移两次就行了。
Code
感觉写了依托……
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 3e5 + 10;
const int inf = 0x3f3f3f3f;
int n[2],K;
struct node{
int l,r;
bool operator < (const node &a) const{
return l < a.l;
}
bool operator < (const int x) const{
return r < x;
}
}a[2][N];
int f[N << 1][2][2];
int search(int id,int x){
return lower_bound(a[id] + 1,a[id] + 1 + n[id],x) - a[id];
}
int find(int id,int x){
int now = search(id,x);
if(a[id][now].l > x) return 0;
return now;
}
int suf(int id,int x){
int now = search(id,x);
if(a[id][now].l <= x) now ++ ;
return now;
}
int main()
{
IOS;
cin >> n[0] >> n[1] >> K;
for(int i=1;i<=n[0];i++){
cin >> a[0][i].l >> a[0][i].r;
a[0][i].r -- ;
}
for(int i=1;i<=n[1];i++){
cin >> a[1][i].l >> a[1][i].r;
a[1][i].r -- ;
}
n[0] ++ ;
n[1] ++ ;
a[0][n[0]] = {inf,inf};
a[1][n[1]] = {inf,inf};
sort(a[0] + 1,a[0] + 1 + n[0]);
sort(a[1] + 1,a[1] + 1 + n[1]);
memset(f,0x3f,sizeof f);
f[!a[0][1].l][0][0] = 0;
int ans = 0;
for(int i=!(a[0][1].l);i <= n[0] + n[1];i ++ ){
for(int _=0;_<2;_++)
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
if(f[i][j][k] >= inf) continue;
int tim = f[i][j][k];
int nw = find(j,tim);
int nxt = suf(j,tim);
int oth = find(j^1,tim+K);
f[i + (oth && (k ^ 1))][j^1][(nw && (find(j,tim+2*K) == nw))] = min(f[i + (oth && (k ^ 1))][j^1][(nw && (find(j,tim+2*K) == nw))],tim + K);
f[i+1][j][(k && oth && (find(j^1,a[j][nxt].l + K) == oth))] = min(f[i+1][j][(k && oth && (find(j^1,a[j][nxt].l + K) == oth))],a[j][nxt].l);
ans = i;
}
}
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号