• noip模拟测试31


    终于有时间写博客了,前面一直咕咕咕都快变成一只公鸡了......这次考试,真的很意外,我在考场上觉得自己打出了T1的正解,样例一拍就过,还跑得嘎嘎快,然后T2,T3码了两个暴力,觉得自己应该能100pts+,结果竟然....爆蛋了,T1思路出现了问题,T2打假了,T3TLE飞起,但是收获还是有的,以后注意多加思考,看看自己的思路有没有正确性,还有,一定要用暴力程序进行验证(暴力不要打假...),样例真是太水了....

    T1 Game

    思路:最大得分可以利用线段树很容易求出,如果没有字典序最大的限制,那么这道题就非常简单,但这只是如果,现在这道题有了双重限制,听某巨佬的叙述,得知一般有双重限制的题一般有两种思考方向,一种是二分,另一种是主席树,那么很显然,这道题我们可以考虑二分的思想,具体来说就是我们先通过线段树求出最大得分,然后考虑这样一件事情,如果有一对点可以对答案造成贡献,那么我们将他们同时删去必然会使更新后的最大得分-1,这样我们就可以进行二分查找了,对于可以造成贡献的点,我们二分出他可以对应的最大的另外一个点,具体实现见代码:

    AC_code
    
    
    #include<bits/stdc++.h>
    #define re register int
    #define ii inline int
    #define iv inline void
    #define lc (rt<<1)
    #define rc (rt<<1|1)
    using namespace std;
    const int N=200010;
    const int INF=1e9+10;
    int n;
    int a[N],b[N];
    multiset<int> sm;
    ii read()
    {
    	int x=0;
    	bool f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-')
    			f=0;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return f?x:(-x);
    }
    struct Segment_Tree
    {
    	int A[N<<2],B[N<<2],sum[N<<2];
    	iv pp(int rt)
    	{
    		int u=min(A[lc],B[rc]);
    		sum[rt]=sum[lc]+sum[rc]+u;
    		A[rt]=A[lc]+A[rc]-u;
    		B[rt]=B[lc]+B[rc]-u;
    	}
    	iv insert(int rt,int l,int r,int p,int x,int y)
    	{
    		if(l==r)
    		{
    			A[rt]+=x;
    			B[rt]+=y;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(mid>=p)
    			insert(lc,l,mid,p,x,y);
    		else
    			insert(rc,mid+1,r,p,x,y);
    		pp(rt);
    	}
    }T;
    int main()
    {
    	const int N=100000;
        n=read();
        for(re i=1;i<=n;i++)
        {
        	a[i]=read();
        	T.insert(1,1,N,a[i],1,0);
        }
        for(re i=1;i<=n;i++)
        {
        	b[i]=read();
        	T.insert(1,1,N,b[i],0,1);
        	sm.insert(b[i]);
        }
        int ans=T.sum[1];
        for(re i=1;i<=n;i++)
        {
        	int l=a[i]+1,r=*(--sm.end()),out=-1;
        	T.insert(1,1,N,a[i],-1,0);
        	while(l<=r)
        	{
        		int mid=(l+r)>>1;
        		T.insert(1,1,N,mid,0,-1);
        		if(T.sum[1]+1==ans)
        		{
        			l=mid+1;
        			out=mid;
        		}
        		else
        			r=mid-1;
        		T.insert(1,1,N,mid,0,1);
        	}
        	if(out!=-1)
        	{
        		--ans;
        		sm.erase(sm.find(out));
        		T.insert(1,1,N,out,0,-1);
        		printf("%d ",out);
        	}
      	else
     
        	{
        		l=1,r=a[i],out;
        		while(l<=r)
        		{
        			int mid=(l+r)>>1;
        			T.insert(1,1,N,mid,0,-1);
        			if(T.sum[1]==ans)
        			{
        				l=mid+1;
        				out=mid;
        			}
        			else
        				r=mid-1;
        			T.insert(1,1,N,mid,0,1);
        		}
        		printf("%d ",out);
        		sm.erase(sm.find(out));
        		T.insert(1,1,N,out,0,-1);
        	}
        }
        return 0;
    }
    
    

    T2 Time

    这道题,或者说是这一种对数列进行排列的题,我是比较不自信的,因为自己不怎么有思路,导致我没有留出时间进行思考,这也是一个教训,逃避不是永久的办法,只有自己敢去想,去做,才能解决问题。
    思路:这道题要求小的数字往两边靠,那很显然,如果要做到最小操作次数就要比较向左还是向右移动更优,可以利用线段树或者树状数组实现,算法的正确性在于,当我们将小数字向边缘移动的时候,我们都会将比他大的数字向中心移动,所以我们只需要利用贪心的思想只考虑使当前最优即可。

    AC_code
    
    #include<bits/stdc++.h>
    #define re register int
    #define ii inline int
    #define iv inline void
    #define lc (rt<<1)
    #define rc (rt<<1|1)
    #define mid ((l+r)>>1)
    using namespace std;
    const int N=1e5+10;
    const int INF=1e9+10;
    int n;
    deque<int>q[N];
    ii read()
    {
    	int x=0;
    	bool f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-')
    			f=0;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return f?x:(-x);
    }
    struct Segment_Tree
    {
    	int sum[N<<2];
    	iv pp(int rt)
    	{
    		sum[rt]=sum[lc]+sum[rc];
    	}
    	iv insert(int rt,int l,int r,int p,int z)
    	{
    		if(l==r)
    		{
    			sum[rt]+=z;
    			return;
    		}
    		if(mid>=p)
    			insert(lc,l,mid,p,z);
    		else
    			insert(rc,mid+1,r,p,z);
    		pp(rt);
    	}
    	ii query(int rt,int l,int r,int L,int R)
    	{
    		if(L>R)
    			return 0;
    		if(L<=l&&r<=R)
    			return sum[rt];
    		if(mid>=R)
    			return query(lc,l,mid,L,R);
    		if(mid<L)
    			return query(rc,mid+1,r,L,R);
    		return query(lc,l,mid,L,R)+query(rc,mid+1,r,L,R);
    	}
    }T;
    int main()
    {
    	int ans=0;
    	n=read();
    	for(re i=1;i<=n;i++)
    	{
    		q[read()].push_back(i);
    		T.insert(1,1,n,i,1);	
    	}
    	for(re i=1;i<=n;i++)
    	{
    		while(!q[i].empty())
    		{
    			int l=q[i].front(),r=q[i].back();
    			int sl=T.query(1,1,n,1,l-1),sr=T.query(1,1,n,1,n)-T.query(1,1,n,1,r);
    			if(sl<=sr)
    			{
    				ans+=sl;
    				T.insert(1,1,n,l,-1);
    				q[i].pop_front();
    			}						
    			else
    			{
    				ans+=sr;
    				T.insert(1,1,n,r,-1);
    				q[i].pop_back();
    			}
    		}
    	}
    	printf("%d\n",ans);
        return 0;
    }
    
    

    T3 Cover

    好吧,这道题怪我没有认真听课,区间两两之间只有包含和不相交的关系满足这个条件,他们的包含关系一定会构成一颗树,那么这道题显然就是一个树形DP(而不是我写的区间DP),记 \(f_{i,j}\)表示在 i 为根的子树中点被覆盖的最多次数为 j 的最优答案,转移
    的时候首先将所有子树的答案直接合并,然后考虑点 i 的贡献 \(f_{i,j}=max(f_{i,j},f_{i,j-1}+val)\) , 这样朴素的DP方程显然时间复杂度会爆炸,考虑优化,我们考虑答案更新的过程,我们利用包含关系构建一颗树,那么我们将子树合并的时候,当前的节点会存储多个val,包括自己本身的,还有自己的儿子合并得到的,这种答案属于同一层,我们需要不同层之间的转移。也就是到达跟节点的时候我们需要不断从左右区间中分别拿出最大值,这就是我们需要的答案,那么我们就可以利用一个 set ,通过一个合并的操作插入值并排序,具体实现见代码:

    AC_code
    
    
    #include<bits/stdc++.h>
    #define re register int
    #define ii inline int
    #define iv inline void
    #define lc (rt<<1)
    #define rc (rt<<1|1)
    #define mid ((l+r)>>1)
    using namespace std;
    const int N=1e5+10;
    const int INF=1e9+10;
    int n;
    deque<int>q[N];
    ii read()
    {
    	int x=0;
    	bool f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-')
    			f=0;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return f?x:(-x);
    }
    struct Segment_Tree
    {
    	int sum[N<<2];
    	iv pp(int rt)
    	{
    		sum[rt]=sum[lc]+sum[rc];
    	}
    	iv insert(int rt,int l,int r,int p,int z)
    	{
    		if(l==r)
    		{
    			sum[rt]+=z;
    			return;
    		}
    		if(mid>=p)
    			insert(lc,l,mid,p,z);
    		else
    			insert(rc,mid+1,r,p,z);
    		pp(rt);
    	}
    	ii query(int rt,int l,int r,int L,int R)
    	{
    		if(L>R)
    			return 0;
    		if(L<=l&&r<=R)
    			return sum[rt];
    		if(mid>=R)
    			return query(lc,l,mid,L,R);
    		if(mid<L)
    			return query(rc,mid+1,r,L,R);
    		return query(lc,l,mid,L,R)+query(rc,mid+1,r,L,R);
    	}
    }T;
    int main()
    {
    	int ans=0;
    	n=read();
    	for(re i=1;i<=n;i++)
    	{
    		q[read()].push_back(i);
    		T.insert(1,1,n,i,1);	
    	}
    	for(re i=1;i<=n;i++)
    	{
    		while(!q[i].empty())
    		{
    			int l=q[i].front(),r=q[i].back();
    			int sl=T.query(1,1,n,1,l-1),sr=T.query(1,1,n,1,n)-T.query(1,1,n,1,r);
    			if(sl<=sr)
    			{
    				ans+=sl;
    				T.insert(1,1,n,l,-1);
    				q[i].pop_front();
    			}						
    			else
    			{
    				ans+=sr;
    				T.insert(1,1,n,r,-1);
    				q[i].pop_back();
    			}
    		}
    	}
    	printf("%d\n",ans);
        return 0;
    }
    
    
    
  • 相关阅读:
    python-Python调用wcf接口
    一个数据驱动的ui自动化框架思路
    selenium分布式部署
    UI自动化-Element is not clickable at point-----问题记录
    idea下载git代码
    windows的hosts文件路径
    端口号
    Hadoop压缩
    MongoDB(单节点)环境配置
    快排
  • 原文地址:https://www.cnblogs.com/WindZR/p/15110242.html
Copyright © 2020-2023  润新知