[POI2007]EGZ-Driving Exam

能到达所有路的充要条件是能到达左右两端的路

用vector反向建边对每条路左右分别求个最长不上升子序列

预处理出每条路向左向右分别需要多建多少路才能到达最左端和最右端

然后跑个\(\Theta(n)\)的尺取法就可以了

本题最长不上升子序列用vector+zkw线段树比二分更加好想?

原来标程的solve函数尺取时出锅(但是居然AC了

感谢我自己回来看题解发现看不懂

现在这题解是真题解了

#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
#include"vector"
using namespace std;

const int MAXN=1<<17;

int n,m,P,K,np;
int f[2][MAXN];
int tree[MAXN<<1];
struct rpg{bool kd;int h,fl,fr;};
vector<rpg> vec[MAXN];

int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||'9'<ch) ch=getchar();
	while('0'<=ch&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
	return x;
}

void calcl()
{
	for(int i=1;i<n;++i){
		for(int j=0;j<vec[i].size();++j){
			if(!vec[i][j].kd){
				int l=MAXN+vec[i][j].h-1,r=MAXN+m+2,maxn=0;
				while(l^r^1){
					if(~l&1) maxn=max(maxn,tree[l^1]);
					if(r&1) maxn=max(maxn,tree[r^1]);
					l>>=1;r>>=1;
				}vec[i][j].fl=maxn+1;
			}
		}for(int j=0;j<vec[i].size();++j){
			if(!vec[i][j].kd){
				for(int k=MAXN+vec[i][j].h;k;k>>=1){
					tree[k]=max(tree[k],vec[i][j].fl);
				}
			}
		}int l=MAXN,r=MAXN+m+2,maxn=0;
		while(l^r^1){
			if(~l&1) maxn=max(maxn,tree[l^1]);
			if(r&1) maxn=max(maxn,tree[r&1]);
			l>>=1,r>>=1;
		}f[0][i+1]=maxn;
	}return;
}

void calcr()
{
	for(int i=n;i>1;--i){
		for(int j=0;j<vec[i].size();++j){
			if(vec[i][j].kd){
				int l=MAXN+vec[i][j].h-1,r=MAXN+m+2,maxn=0;
				while(l^r^1){
					if(~l&1) maxn=max(maxn,tree[l^1]);
					if(r&1) maxn=max(maxn,tree[r^1]);
					l>>=1;r>>=1;
				}vec[i][j].fr=maxn+1;
			}
		}for(int j=0;j<vec[i].size();++j){
			if(vec[i][j].kd){
				for(int k=MAXN+vec[i][j].h;k;k>>=1){
					tree[k]=max(tree[k],vec[i][j].fr);
				}
			}
		}int l=MAXN,r=MAXN+m+2,maxn=0;
		while(l^r^1){
			if(~l&1) maxn=max(maxn,tree[l^1]);
			if(r&1) maxn=max(maxn,tree[r&1]);
			l>>=1,r>>=1;
		}f[1][i-1]=maxn;
	}return;
}

void init()
{
	n=read(),m=read(),P=read(),K=read();
	for(int i=1;i<=P;++i){
		int x=read(),y=read(),z=read();
		if(z) vec[x].push_back((rpg){z^1,y+1});
		else vec[x+1].push_back((rpg){z^1,y+1});
	}calcl();memset(tree,0,sizeof(tree));calcr();
	for(int i=1;i<=n;++i) f[0][i]=i-f[0][i]-1,f[1][i]=n-i-f[1][i];
	return;
}

void solve()
{
	int ans=0,ct=0,cnt=0;
	for(int i=1;i<=n;++i){
		while(ct+1<=n&&f[1][i]+f[0][ct+1]<=K){
			++ct;
			if(!f[0][ct]&&!f[1][ct]) ++cnt;
		}ans=max(ans,ct-i+1-cnt);
		if(!f[0][i]&&!f[1][i]) --cnt;
	}printf("%d\n",ans);
	return;
}

int main()
{
	init();
	solve();
	return 0;
}
posted @ 2018-09-10 16:19  A·H  阅读(209)  评论(0编辑  收藏  举报