博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

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\n",ans);
	}

	return 0;
}

I.Soldier Game(枚举 线段树)

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

\(Solution\)
最大值-最小值,考虑枚举!
区间只有\(2n\)个,可以将\(2n\)个区间排序,从小到大枚举最小值。
\(\min\{\max\}\)表示令最大值最小时的最大值。枚举最小值区间\(i\)后,要用剩下的区间覆盖\(1\sim 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\n",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",n-mx,n-mn);
	}

	return 0;
}

posted @ 2020-10-03 22:21  SovietPower  阅读(249)  评论(0编辑  收藏  举报