zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:旋转子段(数学)

    题目描述

    $ZYL$有$N$张牌编号分别为$1,2,...,N$。他把这$N$张牌打乱排成一排,然后他要做一次旋转使得旋转后固定点尽可能多。如果第$i$个位置的牌的编号为$i$,我们就称之为固定点。旋转可以被认为是将其中的一个子段旋转$180$度,这意味着子段的第一张牌和最后一张牌交换位置,以及第二张牌和倒数第二张牌交换位置,等等。写一个程序,找到旋转子段(子段长度可以为$1$)。


    输入格式

    第一行包含一个整数$N$。
    第二行有$N$个数,第$i$个数表示旋转之前第$i$个位置的牌的编号。


    输出格式

    找到固定点最多的旋转所选的子段,输出旋转之后固定点的个数。


    样例

    样例输入1:

    4
    3 2 1 4

    样例输出1:

    4

    样例输入2:

    2
    1 2

    样例输出2:

    2


    数据范围与提示

    样例解释:

    在样例$1$中,只需要旋转的子段$[3,2,1]$,将排列变成$1 2 3 4$,旋转后所有的牌都为固定点。答案为$4$。
    在样例$2$中,所有的牌已经在固定点,旋转子段$[1]$或者子段$[2]$,答案为$2$。

    数据范围:

    $30\%$的数据满足:$Nleqslant 500$;
    $60\%$的数据满足:$Nleqslant 5,000$;
    $100\%$的数据满足:$1leqslant Nleqslant 500,000$。


    题解

    $30\%$算法:

    裸的暴力,直接搞就行了。

    可以用$reverse$卡常,但是没什么用。

    注意可以不旋转。

    时间复杂度:$Theta(n^3)$。

    期望得分:$30$分。

    实际得分:$35$分。

    $60\%$算法:

    枚举旋转中心和旋转半径,思想类似莫队。

    时间复杂度:$Theta(n^2)$。

    期望得分:$60$分。

    实际得分:$60$分。

    $100\%$算法:

    先来利用反证法来证明一个问题:如果最优翻转区间是$[l,r]$,那么如果$a[l] eq r&&a[r] eq l$,则翻转这个区间$a[l]$和$a[r]$一定都不能成为不动点,对答案一定不能做出贡献,选择区间$[l+1,r-1]$一定不劣。

    所以,我们要找的区间一定满足$a[l]=r||a[r]=l$,也就是说,翻转之后一定有一个点成为不动点。

    根据上面的出的结论,我们还可以知道,答案一定在所有的$[min(a[i],i),max(a[i],i)]$中产生。

    显然仅仅是这样我们还是会超时,那么考虑如何进行优化。

    依次枚举$n$必不可少,我们考虑从查询中入手。

    我们还可以知道,一个点在翻转之后成为不动点,在翻转之前一定满足$a[i]+i=a[j]+j$(式中$i,j$分别表示旋转之前的位置和旋转之后的位置)。

    可以使用$vector$优化空间复杂度。

    时间复杂度:$Theta(nlog n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    $30\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    int a[500001];
    int ans;
    int main()
    {
    	int n;
    	scanf("%d",&n);
    	for(register int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		if(a[i]==i)ans++;
    	}
    	for(register int i=1;i<n;i++)
    		for(register int j=i+1;j<=n;j++)
    		{
    			register int sum=0;
    			reverse(a+i,a+j+1);
    			for(register int k=1;k<=n;k++)
    				if(a[k]==k)sum++;
    			reverse(a+i,a+j+1);
    			ans=max(ans,sum);
    		}
    	printf("%d",ans);
    	return 0;
    }
    

    $60\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    int n,ans,now,res,a[500001];
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) 
    	{
    		scanf("%d",&a[i]);
    		if(a[i]==i)res++;
    	}
    	ans=res;
    	for(int i=1;i<=n;i++)
    	{
    		now=res;
    		for(int j=1;j<=n;j++)
    		{
    			int l=i-j,r=i+j;
    			if(l<=0||r>=n)break;
    			if(a[l]==r)now++;
    			if(a[r]==l)now++;
    			if(a[l]==l)now--;
    			if(a[r]==r)now--;
    			ans=max(ans,now);
    		}
    		now=res;
    		for(int j=1;j<=n;j++)
    		{
    			int l=i-j+1,r=i+j;
    			if(l<=0||r>=n)break;
    			if(a[l]==r)now++;
    			if(a[r]==l)now++;
    			if(a[l]==l)now--;
    			if(a[r]==r)now--;
    			ans=max(ans,now);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    $100\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    int a[2000000],sum1[2000000],sum2[2000000];
    int flag,ans;
    vector<int> g[2000000];
    bool cmp(int x,int y){return abs((x<<1)-flag)<abs((y<<1)-flag);}
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		g[a[i]+i].push_back(i);
    		if(a[i]==i)sum1[i]=sum2[i]=1;
    		sum1[i]+=sum1[i-1];
    	}
    	for(int i=n;i;i--)sum2[i]+=sum2[i+1];
    	for(int i=2;i<=(n<<1);i++)
    		if(!g[i].empty())
    		{
    			flag=i;
    			sort(g[i].begin(),g[i].end(),cmp);
    			for(int j=0;j<g[i].size();j++)
    			{
    				int l=g[i][j];
    				int r=i-g[i][j];
    				if(l>r)swap(l,r);
    				ans=max(ans,sum1[l-1]+sum2[r+1]+j+1);
    			}
    		}
    	cout<<ans<<endl;
    	return 0;
    }
    

    rp++

  • 相关阅读:
    2015-SH项目总结
    2015年总结
    [css]我要用css画幅画(七)
    [css]我要用css画幅画(六)
    [css]我要用css画幅画(五)
    [css]我要用css画幅画(四)
    [css]我要用css画幅画(三)
    程序遇到问题需要关闭
    ASP.NET Core 验证:(二)介绍ASP.NET Core的 Indentity
    ASP.NET Core 验证:(一)概述
  • 原文地址:https://www.cnblogs.com/wzc521/p/11319845.html
Copyright © 2011-2022 走看看