P.S.
补 VP 题。
Description.
卡老师手上拿着两份代码,都有一个分数值。
刚开始卡老师一分都不会,所以两份代码的分数值都是 \(0\)。
每次老K会给卡老师一份代码,卡老师必须且只能把他的一份代码卡成老K给的。
不过每次卡老师为了不被发现,所以每卡完一次都必须保证第一份代码的分数在 \([L_{i,0},R_{i,0}]\) 之间,第二份在 \([L_{i,1},R_{i,1}]\) 之间。
问卡老师能不能卡到最后。
Solution.
首先,我们考虑把他当做一段 \(0,1\) 串。
然后,模拟一下,我们会发现。
a ?
c1 a c1 1
c2 c2 c1 0
c3 c3 c1 0
...
ck ck c1 0
如果有一段极长 \(0\) 串是合法的,当且仅当如下两个条件同时满足。
\[\forall i\in[l,r],L_{i,0}\le c_i\le R_{i,0} \] \[\forall i\in[l,r],L_{i,1}\le c_{l-1}\le R_{i,1} \]极长 \(1\) 同理。
我们观察式子发现,如果从前往后推,其中的 \(C_{l-1}\) 不同会导致不具有单调性。
但是从后往前推我们只需要记录 \(\max\{L_{i-1,?}\}\) 和 \(\min\{R_{i-1,?}\}\) 然后就可以 \(O(1)\) 判断当前是否可行(对于条件 2。
所以我们从后往前,这样对于一个转移点,必定是它后面的一段相邻区间可以转移到它。
这样就具有单调性了,可以优化了。
我们维护 \(0\) 和 \(1\) 分别的最后可行端点,检查能否转移到当前位置。
然后就可以直接 \(O(n)\) 扫一遍的复杂度内 AC 此题。
Coding.
点击查看代码//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}/*}}}*/
const int N=100005;int n,m,l[N][2],r[N][2],c[N],nx[N][2];
int main()
{
//以下代码中,wh 表示最靠前的可行位置,nw 表示当前第一个条件能否满足
//ck 表示当前能否满足两个条件,L,R 分别表示当前最大左界和最小右界
read(n),read(m);int wh[2]={n+1,n+1};for(int i=1;i<=n;i++)
read(c[i]),read(l[i][0]),read(r[i][0]),read(l[i][1]),read(r[i][1]);
int nw[2]={1,1},ck[2]={0,0},L[2]={0,0},R[2]={m,m};for(int i=n;i;i--)
{
nw[0]&=l[i][0]<=c[i]&&c[i]<=r[i][0],nw[1]&=l[i][1]<=c[i]&&c[i]<=r[i][1];
L[0]=max(L[0],l[i][1]),R[0]=min(R[0],r[i][1]),L[1]=max(L[1],l[i][0]),R[1]=min(R[1],r[i][0]);
ck[0]=nw[0]&&(L[0]<=c[i-1]&&c[i-1]<=R[0]),ck[1]=nw[1]&&(L[1]<=c[i-1]&&c[i-1]<=R[1]);
(ck[0]?nx[i][0]=wh[0]:0),(ck[1]?nx[i][1]=wh[1]:0);
if(ck[0]) nw[1]=1,wh[1]=i,L[1]=0,R[1]=m;
if(ck[1]) nw[0]=1,wh[0]=i,L[0]=0,R[0]=m;
}
if(wh[0]>1&&wh[1]>1) return puts("No"),0;else puts("Yes");
for(int i=1,p=wh[1]>1;i<=n;i=nx[i][p],p^=1) for(int j=i;j<nx[i][p];j++) printf("%c ",'0'|p);
return putchar('\n'),0;
}
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}/*}}}*/
const int N=100005;int n,m,l[N][2],r[N][2],c[N],nx[N][2];
int main()
{
//以下代码中,wh 表示最靠前的可行位置,nw 表示当前第一个条件能否满足
//ck 表示当前能否满足两个条件,L,R 分别表示当前最大左界和最小右界
read(n),read(m);int wh[2]={n+1,n+1};for(int i=1;i<=n;i++)
read(c[i]),read(l[i][0]),read(r[i][0]),read(l[i][1]),read(r[i][1]);
int nw[2]={1,1},ck[2]={0,0},L[2]={0,0},R[2]={m,m};for(int i=n;i;i--)
{
nw[0]&=l[i][0]<=c[i]&&c[i]<=r[i][0],nw[1]&=l[i][1]<=c[i]&&c[i]<=r[i][1];
L[0]=max(L[0],l[i][1]),R[0]=min(R[0],r[i][1]),L[1]=max(L[1],l[i][0]),R[1]=min(R[1],r[i][0]);
ck[0]=nw[0]&&(L[0]<=c[i-1]&&c[i-1]<=R[0]),ck[1]=nw[1]&&(L[1]<=c[i-1]&&c[i-1]<=R[1]);
(ck[0]?nx[i][0]=wh[0]:0),(ck[1]?nx[i][1]=wh[1]:0);
if(ck[0]) nw[1]=1,wh[1]=i,L[1]=0,R[1]=m;
if(ck[1]) nw[0]=1,wh[0]=i,L[0]=0,R[0]=m;
}
if(wh[0]>1&&wh[1]>1) return puts("No"),0;else puts("Yes");
for(int i=1,p=wh[1]>1;i<=n;i=nx[i][p],p^=1) for(int j=i;j<nx[i][p];j++) printf("%c ",'0'|p);
return putchar('\n'),0;
}