打败boss的三种方法

题面

第一种

发现只有端点更新答案更优,或者由上面的点直接继承下来,于是增开虚拟点维护答案。为了避免被卡,虚拟点需要在失效时才被用到。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5,L=1e6;
const ll INF=(1ll<<60);
int n,A,B,opt;ll dp[3][L+5],ans=INF,hhh[L+5];
int sta[3][L+5],top[3];//额外开的点
struct node {int l,r;};
vector<node> v[N+5];
vector<int> tmp;
int c[L+5],flag[4*L+5],tag[4*L+5];
void add(int x,int v) {if(!x) return;for(int i=x; i<=L; i+=i&(-i)) c[i]+=v;}
int ask(int x) {int ret=0;for(int i=x; i>=1; i-=i&(-i)) ret+=c[i];return ret;}
void chmin(ll &x,ll y) {x=min(x,y);}
int read()
{
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
int findl(int l,int r,int x,int id)
{
	int ret=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(v[id][mid].l<=x) l=mid+1,ret=mid;
		else r=mid-1;
	}
	return ret;
}
int findr(int l,int r,int x,int id)
{
	int ret=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(v[id][mid].r>=x) r=mid-1,ret=mid;
		else l=mid+1;
	}
	return ret;
}

void pushdown(int k)
{
	if(!tag[k]) return;
	flag[k*2]=flag[k*2+1]=tag[k*2]=tag[k*2+1]=tag[k];
	tag[k]=0;
}
void modity(int k,int l,int r,int x,int y,int v)
{
	if(x>r||y<l) return;
	if(x<=l&&r<=y) {tag[k]=v;flag[k]=v;return;}
	pushdown(k);int mid=(l+r)>>1;
	modity(k*2,l,mid,x,y,v);modity(k*2+1,mid+1,r,x,y,v);
	flag[k]=flag[k*2]|flag[k*2+1];
}
void getall(int k,int l,int r,int x,int y)
{
	if(l==r) {if(flag[k]) tmp.push_back(l);return;}
	pushdown(k);int mid=(l+r)>>1;
	if(x<=mid&&flag[k*2]) getall(k*2,l,mid,x,y);
	if(y>mid&&flag[k*2+1]) getall(k*2+1,mid+1,r,x,y);
}


void update(int t,int x)//用t时刻的x点更新答案 
{
	int l=findl(0,v[t+1].size()-1,x,t+1);
	int r=findr(0,v[t+1].size()-1,x,t+1);
	if(l!=-1&&v[t+1][l].r>=x) hhh[x]=dp[opt][x],modity(1,1,L,x,x,1);
	else
	{
		if(l!=-1) chmin(dp[opt^1][v[t+1][l].r],dp[opt][x]+1ll*(x-v[t+1][l].r)*A);
		if(r!=-1) chmin(dp[opt^1][v[t+1][r].l],dp[opt][x]+1ll*(v[t+1][r].l-x)*B);
	}
}
void pushup(int num)
{
	for(int i=-1,sz=v[num].size(); i<sz; i++)
	{
		int l,r;
		if(i==-1) l=1,r=v[num][0].l; else l=v[num][i].r,r=(i+1==sz?L:v[num][i+1].l);
		tmp.clear();getall(1,1,L,l,r);
		for(int it:tmp)
		{
			chmin(dp[opt][l],hhh[it]+1ll*(it-l)*A);
			chmin(dp[opt][r],hhh[it]+1ll*(r-it)*B);
			hhh[it]=dp[opt^1][it]=INF;
		}
		modity(1,1,L,l,r,0);
	}
}
int main()
{
	memset(hhh,0x3f,sizeof hhh);
	n=read(),A=read(),B=read();
	for(int i=1; i<=n; i++) {int num=read();while(num--) v[i].push_back(node{read(),read()});};
	memset(dp[1],0x3f,sizeof dp[1]);
	for(int num=1; num<n; num++)
	{
		if(num>1)
			for(auto it:v[num])
			{
				if(ask(it.l)==num-1) dp[opt][it.l]=0;
				if(ask(it.r)==num-1) dp[opt][it.r]=0;
				//对于特殊覆盖的判断
			}
		pushup(num);
		for(auto it:v[num])
		{
			if(dp[opt][it.l]!=INF) update(num,it.l),dp[opt][it.l]=INF;
			if(dp[opt][it.r]!=INF) update(num,it.r),dp[opt][it.r]=INF;
		}
		for(auto it:v[num]) add(it.l,1),add(it.r+1,-1);
		if(num==1) memset(dp[opt],0x3f,sizeof dp[opt]);opt^=1;
	}
	pushup(n);
	for(auto it:v[n])
	{
		if(ask(it.l)==n-1) dp[opt][it.l]=0;
		if(ask(it.r)==n-1) dp[opt][it.r]=0;
	}
	for(int i=1; i<=L; i++) chmin(ans,dp[opt][i]);
	printf("%lld\n",ans);
	return 0;
}

注:代码有锅,不能得分。

第二种

题解做法,用两种线段树滚动维护答案。代码咕。

第三种

std做法,好像是用 \(set\) 跑图论。代码还没没看懂。。。

posted @ 2021-10-21 20:10  keepcoder  阅读(67)  评论(0编辑  收藏  举报