zoukankan      html  css  js  c++  java
  • 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;
    }
    

  • 相关阅读:
    USB描述符(转)
    (转)Linux设备驱动之HID驱动 源码分析
    Linux USB 鼠标驱动程序详解(转)
    (转)linux如何获取鼠标相对位置信息
    从零写一个编译器(四):语法分析之构造有限状态自动机
    从零写一个编译器(三):语法分析之几个基础数据结构
    从零写一个编译器(二):语法分析之前置知识
    从零写一个编译器(一):输入系统和词法分析
    自底向上语法分析
    递归下降和LL(1)语法分析
  • 原文地址:https://www.cnblogs.com/SovietPower/p/13775759.html
Copyright © 2011-2022 走看看