The 2018 ACM-ICPC Asia Qingdao Regional Contest (E,I,K)


10.3 The 2018 ACM-ICPC Asia Qingdao Regional Contest

比赛链接

只有自己写的/改的题。


E.Plants vs. Zombies(二分)√

二分答案。最优解就是从左往右,每次走完一个格子需要的步数再向右。
要注意每次要走一步到下一个格子,但如果走(n-1)时满足了(n)的需求就不用再走一步到(n)
因为写法问题WA了n次。。

//502ms	868kB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=2e5+5;

int A[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
bool Check(int n,LL m,LL pur)
{
	LL need=0;
	for(int i=1; i<=n; ++i)
	{
		LL need2=(pur+A[i]-1)/A[i];
		need2-=need+1;
		if(need2<=0) i!=n?(--m):(!need2/*还需一步到n*/&&--m), need2=0;
		else m-=need2*2+1;
		if(m<0) return 0;
		need=need2;
	}
	return 1;
}

int main()
{
	for(int T=read(); T--; )
	{
		int n=read(); LL m; scanf("%lld",&m);
		for(int i=1; i<=n; ++i) A[i]=read();
		LL l=0,r=1e17,mid,ans=0;
		while(l<=r)
			if(Check(n,m,mid=l+r>>1)) ans=mid, l=mid+1;
			else r=mid-1;
		printf("%lld
",ans);
	}

	return 0;
}

I.Soldier Game(枚举 线段树)

(Description)
(n)个物品,每个价值为(a_i)。现在要将每个物品或相邻的两个物品划分为一组,一组的权值为组中物品价值之和(每个物品属于且只属于一组)。
求划分后组的最大权值-最小权值的最小值。
(nleq 10^5, sum nleq 10^6)

(Solution)
最大值-最小值,考虑枚举!
区间只有(2n)个,可以将(2n)个区间排序,从小到大枚举最小值。
(min{max})表示令最大值最小时的最大值。枚举最小值区间(i)后,要用剩下的区间覆盖(1sim n)且使最大值最小,并求一个(min{max}),然后把区间(i)删掉(不能再作为最大值区间)。
最简单的是一个(O(n))DP。可以发现这是一个每次删除一个区间,且答案与递推顺序无关(可以合并区间)的DP,可以用线段树维护。
线段树每个区间维护四个值:
(lmx):包含左端点但不包含右端点的区间(min{max})
(rmx):包含右端点但不包含左端点的区间(min{max})
(mx):左右端点都不包含的区间(min{max})
(ans):包含左右端点(即整个区间)的(min{max})
就可以合并区间了(初始化即长度为(1)的区间,合并区间时要用到长度为(2)的区间)。
删除区间的时候可以直接将区间赋值为(INF)(一定不会取)。
还有一个点是,最优解中最大值和最小值区间一定不会重叠(除了n=1或2),所以可以直接查询更新再删除(大概是这样)。

PS: 单点初始化时用长度为1,2的区间举个例子,lmx,rmx不存在就是-INF(取max不影响答案的取值),mx不合法就是INF(长度为1的区间不能用mx更新)。
最小的区间是-2e9,最大的区间可能为1e9,即求答案过程中可能爆int(虽然最终答案int内)。

//299ms	6768kB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define mp std::make_pair
#define pr std::pair<int,int>
typedef long long LL;
const int N=1e5+5,INF=2e9+10;

int A[N],B[N];
pr Ref[N<<1];
struct Segment_Tree
{
	#define S N<<2
	int lmx[S],rmx[S],mx[S],ans[S];
	#undef S
	#define ls rt<<1
	#define rs rt<<1|1
	#define lson l,m,ls
	#define rson m+1,r,rs
	inline void Update(int rt,int mid)//B:长度为2的区间 B[mid]就是合并时区间相交段的值!
	{
		int l=ls,r=rs;
		lmx[rt]=std::min(std::max(lmx[rs],ans[ls]),std::max(mx[rs],std::max(B[mid],lmx[ls])));
		rmx[rt]=std::min(std::max(rmx[ls],ans[rs]),std::max(mx[ls],std::max(B[mid],rmx[rs])));
		mx[rt]=std::min(std::max(rmx[ls],lmx[rs]),std::max(mx[ls],std::max(B[mid],mx[rs])));
		ans[rt]=std::min(std::max(ans[ls],ans[rs]),std::max(lmx[ls],std::max(B[mid],rmx[rs])));
	}
	void Build(int l,int r,int rt)
	{
		if(l==r)
		{
			lmx[rt]=-INF, rmx[rt]=-INF, mx[rt]=INF/*!*/, ans[rt]=A[l];
			return;
		}
		int m=l+r>>1;
		Build(lson), Build(rson), Update(rt,m);
	}
	void Modify(int l,int r,int rt,int p)
	{
		if(l==r)
		{
			ans[rt]=A[l];
			return;
		}
		int m=l+r>>1;
		p<=m ? Modify(lson,p) : Modify(rson,p);
		Update(rt,m);
	}
}T;

inline int read()
{
	int now=0,f=1;register char c=gc();
	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now*f;
}

int main()
{
	for(int Ts=read(); Ts--; )
	{
		int n=read(),cnt=n+n-1;
		for(int i=1; i<=n; ++i) Ref[i]=mp(A[i]=read(),i);
		for(int i=1; i<n; ++i) Ref[i+n]=mp(B[i]=A[i]+A[i+1],i+n);
		std::sort(Ref+1,Ref+1+cnt);

		LL ans=1e14;
		T.Build(1,n,1);
		for(int i=1; i<=cnt && T.ans[1]!=INF; ++i)
		{
			ans=std::min(ans,1ll*T.ans[1]-Ref[i].first);
			int t=Ref[i].second;
			t<=n ? (A[t]=INF,T.Modify(1,n,1,t)) : (B[t-n]=INF,T.Modify(1,n,1,t-n));
		}
		printf("%lld
",ans);
	}

	return 0;
}
/*
1 4
1000000000 1000000000 -1000000000 -1000000000
*/

K.Airdrop(思路 枚举)

(Description)
二维平面上有(n)个玩家,每个玩家在((x_i,y_i))处。有一个补给箱掉落在((X,Y))处,其中(Y)已知(X)不确定。
每个玩家在一个单位时间内会向补给箱移动一个单位,移动规则:向上下移动到和(Y)同一横坐标后再左右,直到到补给箱处。若两个或多个玩家同时在非补给箱位置的某处相遇则会全部出局。
(X)不确定情况下出局的最多人数和最少人数。

(Solution)
这题过的人少但不难。。
易知两个或多个人相遇的条件是,他们到((X,Y))的曼哈顿距离相同,即(|x_i-X|+|y_i-Y|)相同。
考虑枚举(X)(最多考虑(2n)个),对于((X,Y))左边的人,若相遇,则有(|X-x_i|+|y_i-Y|)相同,也就是(|y_i-Y|-x_i)相同。从左到有枚举(X)并记录某个值是否出现即可。
对于(X)右边的就是(|y_i-Y|+x_i)相同。

然后是某些细节:三个曼哈顿距离相同的点(i,j,k),前两个可能会早就相遇然后out,第三个不会出局。可以发现要么是前两个(x)相同,要么是(x_i<x_j<x_k)(k)不出局。对于前一种情况记录每个值的(las),后一种情况记一个(tag)即可。
注意下多个人同时出局的情况。
此外我这个写法要注意,(x)相同的点只能在(X)枚举到(x)时更新一次答案(否则不对)。
写的有些乱,不多说了。。自己模拟下就ok。

//309ms	4172kB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=1e5+5,M=3e5+15,lim=1e5+2;

int tag[M],tmp[M],las[M];
struct Node
{
	int val,x;
	bool operator <(const Node &a)const
	{
		return x==a.x?val<a.val:x<a.x;
	}
}A[M];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}

int main()
{
	for(int Ts=read(); Ts--; )
	{
		int n=read(),y=read();
		for(int i=1,X,Y; i<=n; ++i) X=read(), Y=read(), A[i]=(Node){std::abs(Y-y)-X+lim,X};//[-1e5,1e5)->(0,2e5]
		std::sort(A+1,A+1+n);
		int cnt=n;
		for(int i=1; i<n; ++i) if(A[i].x+1<A[i+1].x) A[++cnt]=(Node){0,A[i].x+1};
		A[++cnt]=(Node){0,0}, A[++cnt]=(Node){0,A[n].x+1}, std::sort(A+1,A+1+cnt);

		A[0].x=A[1].x-1, A[cnt+1].x=A[cnt].x+1;
		for(int i=1,ans=0,t,x,j,a; i<=cnt; i=j)
		{
			for(j=i,a=ans; A[j].x==A[i].x; ++j)
			{
				tmp[j]=a;
				if(t=A[j].val)
				{
					x=A[j].x;
					if(las[t])
						if(las[t]==x) tag[t]?++ans:ans+=2, las[t]=0, tag[t]=0;
						else if(!tag[t]) ans+=2, las[t]=x, tag[t]=1;
						else las[t]=x, tag[t]=0;
					else las[t]=x;
				}
			}
		}
		for(int i=1,t; i<=cnt; ++i) tag[t=A[i].val]=0, las[t]=0, t&&(A[i].val+=2*A[i].x);//(0,2e5)->(1e5,3e5)

		int mn=N,mx=0;
		for(int i=cnt,ans=0,t,x,j; i; i=j)
		{
			mn=std::min(mn,ans+tmp[i]), mx=std::max(mx,ans+tmp[i]);
			for(j=i; A[j].x==A[i].x; --j)
			{
				if(t=A[j].val)
				{
					x=A[j].x;
					if(las[t])
						if(las[t]==x) tag[t]?++ans:ans+=2, las[t]=0, tag[t]=0;
						else if(!tag[t]) ans+=2, las[t]=x, tag[t]=1;
						else las[t]=x, tag[t]=0;
					else las[t]=x;
				}
			}
		}
		for(int i=1; i<=cnt; ++i) tag[A[i].val]=0, las[A[i].val]=0;
		printf("%d %d
",n-mx,n-mn);
	}

	return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/13775759.html