• 【纪中集训】2019.08.02【NOIP提高组】模拟 A 组TJ


    ( ewcommand{RNum}[1]{uppercaseexpandafter{ omannumeral #1 elax}})

    T1

    • 一道可以暴力撵标算的题……

    Description

    • 给定二维平面上(N(≤60000))个有权值的点((X_i,Y_i)(in[0,10^9])),点权(Z_iin[0,10^9])
    • (M(≤10000))次操作,操作(Ⅰ)是询问一个边平行于坐标轴的矩形中权值第(K)小的点,操作(Ⅱ)是交换两个点的权值。
    • 时限10s。

    SolutionⅠ

    • 既然时限10s,那肯定要想到暴力。
    • 最朴素暴力应该是找出所有在矩形中的点,快排,取第(K)小。这样是(O(NMlog_2N))的,实测会(TLE),只有52.4pts。
    • 考虑(O(N))找第(K)小。可以先将权值离散化,然后把快排改为桶牌,这样就变成(O(Nlog_2N+NM))了。直接交似乎是3~4s;吸臭氧后可以达到1s+。
    Code
    #include <cstdio>
    #include <algorithm>
    #define fo(i,a,b) for(i=a;i<=b;i++)
    #define O3 __attribute((optimize("-O3")))
    #define NO {puts("It doesn't exist."); continue;}
    using namespace std;
    
    const int N=81e3;
    int i,j,n,m,n1,Z[N],pos[N],x,y,x1,y1,k,l,r,mid,cnt[N],ss;
    struct city{int x,y,z,i;}a[N];
    
    O3 void read(int&x)
    {
    	char c=getchar(); x=0;
    	for(;c<48|c>57;c=getchar());
    	for(;c>47&c<58;c=getchar()) x=x*10+(c^48);
    }
    
    O3 bool cmpx(city a,city b) {return a.x<b.x;}
    
    O3 bool cmpz(int x,int y) {return a[x].z<a[y].z;}
    
    O3 int main()
    {
    	read(n),read(m);
    	fo(i,1,n) read(a[a[i].i=i].x),read(a[i].y),read(a[i].z),Z[i]=a[i].z;
    	sort(Z+1,Z+n+1), n1=unique(Z+1,Z+n+1)-Z-1;
    	sort(a+1,a+n+1,cmpx);
    	fo(i,1,n) pos[a[i].i]=i, a[i].z=lower_bound(Z+1,Z+n1+1,a[i].z)-Z;
    	while(m--)
    	{
    		char c=getchar();
    		while(c!='Q'&c!='S') c=getchar();
    		read(x),read(y);
    		if(c=='Q')
    		{
    			read(x1),read(y1),read(k);
    			if(x>x1) swap(x,x1);
    			if(y>y1) swap(y,y1);
    			l=1,r=n;
    			while(l<r)
    			{
    				mid=l+r>>1;
    				a[mid].x>=x?r=mid:l=mid+1;
    			}
    			if(n-l+1<k) NO
    			fo(i,0,n1) cnt[i]=0;
    			fo(i,l,n)
    			{
    				if(a[i].x>x1) break;
    				if(y<=a[i].y&a[i].y<=y1) cnt[0]++,cnt[a[i].z]++;
    			}
    			if(cnt[0]<k) NO
    			fo(i,1,n1)
    			{
    				if(cnt[i]>=k) break;
    				k-=cnt[i];
    			}
    			printf("%d
    ",Z[i]);
    		}
    		else swap(a[pos[x+1]].z,a[pos[y+1]].z);
    	}
    }
    

    SolutionⅡ

    • 这题实际上是李超12年出的,他当时的做法是分块+远古数据结构划分树。
    • 我们可以将点按(x)坐标分(sqrt N)块,同一块内按(y)坐标排序并构建划分树;这样一来,对于一个询问,它至多覆盖了(sqrt N)个整块和2个散块。我们可以同时处理所有整块,就在划分树上跑;散块则暴力处理。
    • 这样一来,我们就可以达到(O(N^{1.5}+MN^{0.5}log_2N^{0.5}))效率。
    • 代码复杂度翻若干倍。

    SolutionⅢ

    • 既然是求第(K)小,自然可以想到整体二分……
    • 类比一下求区间第(K)小的做法:我们先离线,把所有操作放到一起进行整体二分答案,这就变成了一个一维偏序问题;那这题也可以类似地转化为整体二分+二维偏序。再打一个带修主席树!
    • 时间复杂度达到了惊人的(O((N+M)log_2^3 N))
    • 代码复杂度翻若干倍。

    T2

    Description

    • 有个好(song)玩(ming)的游戏,有(N(≤10^8))个关卡,初始有(Q(≤5))条命。
    • 每通过一个关卡,会得到(u)分和1条命,生命上限为(Q)。其中(u=min()最近一次连续通过的关数(,R))
    • 设通过一关的概率为(p(in[0,1])),求最小的(p)使得期望得分超过(S)

    Solution

    • 我原本以为如果(p)很小,算到后面会爆精度,需要什么奇技淫巧……
    • 结果不需要考虑这些问题。

    • 首先,我们可以预处理出所有可能的状态(当前的(HP)与当前的(u))。极限数据时也就36种。
    • 可以想到二分答案+(DP)判可行:设(f[i][s])表示当前过了第(i)关,状态为(s)的概率。这样的话,我们要一边转移一边更新答案,所以可以把答案设为状态0加入转移。
    • 但是(N)太大,所以矩乘优化。
    • 设二分次数为(L),状态数为(S),则时间复杂度:(O(LS^3log_2N))

    Code

    #include <cstdio>
    #include <cstring>
    #define min(x,y) (x<y?x:y)
    #define S(a) memset(a,0,sizeof a);
    #define C(a,b) memcpy(a,b,sizeof a);
    #define fo(i,a,b) for(i=a;i<=b;i++)
    using namespace std;
    typedef double db;
    
    const int S=37;
    int i,j,k,x,y,n,R,Q,s,rain,g[S][S],l,r,mid,tmp;
    db p,t[S][S],t1[S][S],a[S],a1[S];
    
    void work()
    {
    	S(t) S(a)
    	t[0][0]=a[g[Q][0]]=1;
    	fo(i,1,Q)
    		fo(j,0,R)
    			if(x=g[i][j])
    			{
    				t[0][x]=p*min(j+1,R);
    				if(y=g[min(i+1,Q)][min(j+1,R)]) t[y][x]=p;
    				t[g[i-1][0]][x]=1-p;
    			}
    	for(tmp=n; tmp; tmp>>=1)
    	{
    		if(tmp&1)
    		{
    			C(a1,a)
    			fo(i,0,rain)
    			{
    				a[i]=0;
    				fo(j,0,rain) a[i]+=t[i][j]*a1[j];
    			}
    		}
    		C(t1,t)
    		fo(i,0,rain) fo(j,0,rain)
    		{
    			t[i][j]=0;
    			fo(k,0,rain) t[i][j]+=t1[i][k]*t1[k][j];
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d%d",&n,&R,&Q,&s);
    	fo(i,0,Q) 
    		fo(j,0,R) 
    			if(i>=j||i==Q) g[i][j]=++rain;
    	l=0, r=1e7;
    	while(l<r)
    	{
    		mid=l+r>>1;
    		p=mid/1e7;
    		work();
    		a[0]>s ? r=mid : l=mid+1;
    	}
    	p=l/1e7;
    	work();
    	a[0]>s ? printf("%.6lf",p) : puts("Impossible.");
    }
    

    T3

    Description

    • 给定二维平面上(N(≤10^5))个点((X_i,Y_i)(in[0,10^8])),选3个点,求它们两两之间的曼哈顿距离和的最大值和最小值。

    Solution

    • 3个点的曼哈顿距离和实际上是(2*(X_{max}-X_{min}+Y_{max}-Y_{min})),即3点的外接矩形的边长。那么1个点可以确定其中1~2项。
    • 最大值其实很好求。因为对于一个点而言,它可以确定的总共有8种。我们求出这8种的最大值,配对一下即可。而由于是找最大值,所以在(N>3)的时候,一定不会算重复点。
    • 难点就在求最小值了。

    • 我们可以将其划分为两种情况:情况Ⅰ是1个点确定了2项,剩下2个点各确定1项;情况Ⅱ是2个点确定2项,剩下1个点什么也没确定。
    • 情况Ⅰ又可细分为4种小情况,这里只考虑1个点确定右上角(即该点为((X_{max},Y_{max})))的情况(剩下的可以通过旋转转化而来)。那么还有剩下2个点分别是((X_{min},Y))((Y,X_{min}))
    • 我们按(x)坐标对点排序,并以(x)轴建立线段树,维护三个值:(X_{min},Y_{min},min(X_{min}+Y_{min}))。扫描线,当扫到一个点((X_i,Y_i))时,先查询它的答案,再用(Y_i)区间修改,最后用(X_i)单点修改。注意区间修改时,如果某区间没有点则不修改;并且只有区间修改时才更新答案。这样就不会自己影响到自己。
    • 情况Ⅱ也可细分为2种小情况,也是可以旋转转化。这个比较简单,在此不讲了。

    • 总时间复杂度:(O(Nlog_2N))

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define A v*2
    #define B A+1
    #define L A,l,m
    #define R B,m+1,r
    #define MAX(x,y) if(x<y)x=y
    #define fo(i,a,b) for(i=a;i<=b;i++)
    #define MS(a,x) memset(a,x,sizeof a);
    using namespace std;
    
    const int N=11e4,S=N<<3,inf=0x33333333;
    int i,n,x,y,nX,X[N],nY,Y[N],a1,a2,a3,a4,a5,a6,a7,a8,tx[S],ty[S],tag[S],t[S],mt[S];
    bool tv[S];
    struct rain{int x,y;}a[N];
    
    inline int min(const int&x,const int&y) {return x<y?x:y;}
    inline int max(const int&x,const int&y) {return x>y?x:y;}
    
    inline bool cmp(const rain&a,const rain&b) {return a.x<b.x|a.x==b.x&a.y<b.y;}
    
    inline void MIN(int&a,const int&b) {if(a>b)a=b;}
    
    inline void New(const int&v) {if(!tv[v]) tv[v]=1;}
    
    inline void update(const int&v)
    {
    	if( tv[A]&!tv[B]) tx[v]=tx[A], ty[v]=ty[A], t[v]=t[A], mt[v]=mt[A];
    	else
    	if(!tv[A]& tv[B]) tx[v]=tx[B], ty[v]=ty[B], t[v]=t[B], mt[v]=mt[B];
    	else
    	tx[v]=min(tx[A],tx[B]), ty[v]=min(ty[A],ty[B]), t[v]=min(t[A],t[B]), mt[v]=max(mt[A],mt[B]);
    }
    
    inline void mask(const int&v,const int&y) 
    {
    	if(mt[v]>tx[v]+y) mt[v]=tx[v]+(ty[v]=tag[v]=y), MIN(t[v],mt[v]);
    }
    
    inline void push(const int&v)
    {
    	if(tag[v]==inf) return;
    	New(A), mask(A,tag[v]);
    	New(B), mask(B,tag[v]);
    	tag[v]=inf;
    }
    
    int que(int v,int l,int r)
    {
    	if(!tv[v]|tx[v]==inf|ty[v]==inf|a[i].y<l) return inf;
    	if(a[i].y>=r) return t[v];
    	push(v);
    	int m=l+r>>1;
    	return min(que(L),que(R));
    }
    
    void mdf(int v,int l,int r)
    {
    	if(!tv[v]|a[i].y>r) return;
    	if(a[i].y<=l) {mask(v,-y); return;}
    	push(v);
    	int m=l+r>>1;
    	mdf(L), mdf(R);
    	update(v);
    }
     
    void add(int v,int l,int r)
    {
    	New(v);
    	if(l==r) {tx[v]=-x; return;}
    	push(v);
    	int m=l+r>>1;
    	a[i].y<=m ? add(L) : add(R);
    	update(v);
    }
    
    void work1()
    {
    	sort(a+1,a+n+1,cmp);
    	MS(tv,0) MS(tx,51) MS(ty,51) MS(tag,51) MS(t,51) MS(mt,50)
    	fo(i,1,n)
    	{
    		x=X[a[i].x], y=Y[a[i].y];
    		MIN(a1,2*(que(1,1,nY)+x+y));
    		mdf(1,1,nY);
    		add(1,1,nY);
    	}
    }
    
    int Que(int v,int l,int r,bool k)
    {
    	if(!tv[v]|a[i].y<l) return inf;
    	if(a[i].y>=r) return k?t[v]:tx[v];
    	int m=l+r>>1;
    	return min(Que(L,k),Que(R,k));
    }
    
    void Mdf(int v,int l,int r,int pv,bool k)
    {
    	New(v);
    	if(l==r) {k?MIN(t[v],pv):MIN(tx[v],pv); return;}
    	int m=l+r>>1;
    	a[i].y<=m ? Mdf(L,pv,k) : Mdf(R,pv,k);
    	tx[v]=min(tx[A],tx[B]), t[v]=min(t[A],t[B]);
    }
    
    void work2()
    {
    	MS(tv,0) MS(tx,51) MS(t,51)
    	fo(i,1,n)
    	{
    		x=X[a[i].x], y=Y[a[i].y];
    		MIN(a1,2*(Que(1,1,nY,1)+x+y));
    		Mdf(1,1,nY,Que(1,1,nY,0),1);
    		Mdf(1,1,nY,-x-y,0);
    	}
    }
    
    void rotate() 
    {
    	fo(i,1,n) a[i]={-Y[a[i].y],X[a[i].x]};
    	fo(i,1,n) X[i]=a[i].x, Y[i]=a[i].y;
    	sort(X+1,X+n+1), nX=unique(X+1,X+n+1)-X-1;
    	sort(Y+1,Y+n+1), nY=unique(Y+1,Y+n+1)-Y-1;
    	fo(i,1,n) a[i]={lower_bound(X+1,X+nX+1,a[i].x)-X,lower_bound(Y+1,Y+nY+1,a[i].y)-Y};	
    }
    
    int main()
    {
    	scanf("%d",&n);
    
    	a1=a2=a3=a4=a5=a6=a7=a8=-inf;
    	fo(i,1,n)
    	{
    		scanf("%d%d",&X[i],&Y[i]), a[i]={X[i],Y[i]};
    		MAX(a1, X[i]+Y[i]);
    		MAX(a2,-X[i]+Y[i]);
    		MAX(a3,-X[i]-Y[i]);
    		MAX(a4, X[i]-Y[i]);
    		MAX(a5,-X[i]); MAX(a6,X[i]);
    		MAX(a7,-Y[i]); MAX(a8,Y[i]);
    	}
    	a1=2*max(max(a1+a3,a2+a4),max(max(a1+a5+a7,a2+a6+a7),max(a3+a6+a8,a4+a5+a8)));
    	printf("%d
    ",a1);
    	
    	a1=inf;
    	sort(X+1,X+n+1), nX=unique(X+1,X+n+1)-X-1;
    	sort(Y+1,Y+n+1), nY=unique(Y+1,Y+n+1)-Y-1;
    	fo(i,1,n) a[i]={lower_bound(X+1,X+nX+1,a[i].x)-X,lower_bound(Y+1,Y+nY+1,a[i].y)-Y};
    	work1(), work2(), rotate();
    	work1(), work2(), rotate();
    	work1(), rotate(), work1();
    	printf("%d",a1);
    }
    
  • 相关阅读:
    Kafka 集群搭建 (自用)
    Kafka 简单实验二(Python实现简单生产者消费者)
    Kafka 简单实验一(安装Kafka)
    Spark下载与入门(Spark自学二)
    Spark导论(Spark自学一)
    MongoDB分片集群新增分片(自用)
    Python 获取图片文件大小并转换为base64编码
    Python 获取图片格式及像素宽高信息
    MongoDB集群设置集合分片生效及查看集合分片情况
    商城购物车的实现设计思想
  • 原文地址:https://www.cnblogs.com/Iking123/p/11294139.html
Copyright © 2020-2023  润新知