题解 P6313 【[eJOI2018]护照】
很有趣的题
思路部分来自 @StudyingFather(Loj)
因为
考虑到
对于当前转移点
1、看有哪些出国的时间段包含了
2、然后考虑包含在
注意,注意,注意!这里两种状态是互相影响的
然后考虑时间复杂度,貌似是
但是不能停留在可能,我们尽量优化到
具体实现看代码:
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
#define maxn 55
#define maxN 5005005
#define INF 2000000002
inline int read(){
int r=0,f=0;
char c;
while(!isdigit(c=getchar()))f|=(c=='-');
while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
return f?-r:r;
}
struct LR{
int l,r,t;
int num;
}lr[2][maxn];
int n,p,s[2],ans[maxn][2];
int pre[maxN],f[maxN];
inline bool cmp1(LR x,LR y){
return x.l<y.l;
}
inline bool cmp2(LR x,LR y){
return x.t<y.t;
}
inline void calc(int x,int i){
if(f[x]==INF)return;
while(x){
int j=pre[x];
int num=lr[1][j].num;
ans[num][0]=i;
ans[num][1]=f[x]-lr[1][j].t;
x^=(1<<(num-1));//注意这里是num,理由见下
}
}
int main(){
n=read(),p=read();
for(int i=1;i<=n;i++){
lr[0][i].l=read();
lr[0][i].r=lr[0][i].l+read()-1;
lr[0][i].t=read(),lr[0][i].num=i;
lr[1][i]=lr[0][i];//copy一下
}
sort(lr[0]+1,lr[0]+1+n,cmp1);
sort(lr[1]+1,lr[1]+1+n,cmp2);
for(int i=0;i<(1<<n);i++)f[i]=INF;
f[0]=1;
for(int i=0;i<(1<<n);i++){
if(f[i]==INF)continue;
int t=f[i];
s[0]=s[1]=1;
for(int j=1;j<=n;j++){
//注意j是算枚举t从小到大,所以是lr[1][j]
if(i&(1<<(lr[1][j].num-1)))continue;
while(1){
//这里是按l,所以是lr[0][s[]]
while(s[0]<=n&&lr[0][s[0]].r<t)s[0]++;
while(s[1]<=n&&(lr[0][s[1]].l<t||!(i&(1<<(lr[0][s[1]].num-1)))))s[1]++;
//因为两种排序会导致顺序不同,所以这里的状态是用原本的num,这样方便统一
if(s[0]<=n&&lr[0][s[0]].l<=t){
t=lr[0][s[0]].r+1;//两种情况
continue;
}
if(s[1]<=n&&t+lr[1][j].t>=lr[0][s[1]].l){
t=lr[0][s[1]].r+1;
continue;
}
break;
}
if(t+lr[1][j].t<lr[1][j].l){
int to=i|(1<<(lr[1][j].num-1));
//这里记得也是num
if(f[to]>t+lr[1][j].t){
f[to]=t+lr[1][j].t;
pre[to]=j;
//这里要存j,不然无法减去t[j]
}
}
}
}
if(p==1)calc((1<<n)-1,1);
else {//P=2时取互补的两个状态即可
for(int i=0;i<(1<<n);i++){
if(f[i]!=INF&&f[(1<<n)-1-i]!=INF){
calc(i,1);
calc((1<<n)-1-i,2);
break;
}
}
}
if(!ans[1][0])return puts("NO"),0;
puts("YES");
for(int i=1;i<=n;i++)
printf("%d %d\n",ans[i][0],ans[i][1]);
return 0;
}