P10052 [CCO 2022] Double Attendance

洛谷

根据常规的动态规划思路,我们可以在状态中记录 \(dp_{i,j,k}\) 表示目前时间为 \(i\),在教室 \(j\)\(k\) 表示到达另一个教室时放的是否之前已经看过了时看到的最大数量。

但是时间这一维很大,不可能记在状态里,并且由于换教室需要时间,所以很难除去没有用的时间。

那么就考虑不把时间记在状态里,而是把时间记在动态规划值里面,由于在其它条件相同时,时间越小明显越好,并且看过的数量范围比较小,可以直接记在状态里。

那么就可以有一值等到这个教室的下一个和到另一个教室两个选择。

但是这样就会有个新的问题,我到另一个教室后可能并没有使答案增加。

考虑一下如果没有增加,我们就直接回原来这个教室了,那么相当于白跑了一趟。

那么我们就要求到另外一个教室后直接把状态记在下一次放幻灯片的时间即可。

这样就不需要考虑转移时环的情况了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e16;
int read(){
	char c=getchar();int x=0;bool f=0;
	while(c>'9'||c<'0'){
		if(c=='-')f=1;
		c=getchar();
	}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
	if(f)return -x;
	return x;
}
int n[2],K,dp[600005][2][2],ans;
struct P{
	int l,r;
}a[2][300005];
bool cmp(P a,P b){
	return a.l<b.l;
}
int bina(int p,int x){
	int l=1,r=n[p];
	while(l<=r){
		int mid=l+r>>1;
		if(a[p][mid].r<x)l=mid+1;
		else r=mid-1;
	}
	return l;
}
int find(int p,int x){
	int v=bina(p,x);
	if(a[p][v].l<=x)return v;
	return 0;
}
int find2(int p,int x){
	int v=bina(p,x);
	return v+(a[p][v].l<=x);
}
void update(int &x,int y){
	x=min(x,y);
}
signed main(){
	cin>>n[0]>>n[1]>>K;
	n[0]++,n[1]++;
	a[0][n[0]]={inf,inf},a[1][n[1]]={inf,inf};
	for(int i=1;i<n[0];i++)a[0][i].l=read(),a[0][i].r=read()-1;
	for(int i=1;i<n[1];i++)a[1][i].l=read(),a[1][i].r=read()-1;
	sort(a[0]+1,a[0]+n[0]+1,cmp),sort(a[1]+1,a[1]+n[1]+1,cmp);
	int st=a[0][1].l?0:1;
	memset(dp,0x3f,sizeof(dp));
	dp[st][0][0]=0;
	for(int i=st;i<=n[0]+n[1];i++){
		for(int j=0;j<2;j++){
			for(int k=0;k<2;k++){
				int x=dp[i][j][k];
				if(x>=inf)continue;
				int p=find(j,x),nxt1=find(j^1,x+K),nxt2=find2(j,x);
				if(nxt1&&!k)update(dp[i+1][j^1][p&&find(j,x+2*K)==p],x+K);
				else {
					int y=a[j^1][find2(j^1,x+K)].l;
					update(dp[i+1][j^1][p&&find(j,y+K)==p],y);
				}
				update(dp[i+1][j][nxt1&&k&&find(j^1,a[j][nxt2].l+K)==nxt1],a[j][nxt2].l);
				ans=i;
			}
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2026-02-05 10:17  huhangqi  阅读(3)  评论(0)    收藏  举报