Codeforces Round #636 (Div. 3)

      解析:把式子的x分出来,就是x*(2^0+2^1......+2^k-1)=n。所以先累加括号里的值打个表,只要n能整除它就输出

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=1e5+20;
ll a[maxn];
int main()
{
    ll n;
    int t;
    cin>>t;
    a[1]=1;
    ll sum = 1;
    for(int i=2;i<=1e9;i++)
    {
        sum*=2;
        a[i]=a[i-1]+sum;
        if(a[i]>=1e9)
            break;
    }
    while(t--)
    {
        cin>>n;
        for(int i=2;;i++)
        {
            if(n%a[i]==0)
            {
                cout<<n/a[i]<<endl;break;
            }
        }
    }
}

     题意:给出数组偶数长度n,能否输出一个:左边一半为偶数,右边一半为奇数,两边和相等。而且不存在重复数字。

     解析:n/2的结果如果不是偶数,那么就肯定构造不出。比如6/2=3,奇数个奇数相加是奇数,是没办法等于偶数部分的和的。偶数部分直接按2,6,10构造,之间的差距不能为2,奇数部分直接参考左边对应位置,每次+1,-1即可,偶数差>2保证了奇数不会出现相等。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=2e5+20;
ll a[maxn];
ll b[maxn];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        int md=n/2;
        if(md%2!=0)
            cout<<"NO"<<endl;
        else
        {
            cout<<"YES"<<endl;
            ll k=2,tot=0;
            ll sum=0;
            for(int i=1;i<=md;i++)
            {
                sum+=k;
                b[i]=k;        
                k+=4;
            }
            int ok=0;
            for(int i=md+1;i<=n;i++)
            {
                if(!ok)
                {
                    b[i]=b[i-md]-1;
                    ok=1;
                }
                else
                {
                    b[i]=b[i-md]+1;
                    ok=0;
                }
            }
            for(int i=1;i<n;i++)
                cout<<b[i]<<" ";
                cout<<b[n]<<endl;
        }
    }
}

     题意:给出一组数,找出最长的正负交替的子序列(这里是通过删除某些数而得到的子序列),输出它们的最大和。

     解析:找每一段连续正或负的最大值,累加即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f3f;
ll a[maxn];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        ll maxx=0;
        ll sum=0;
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
            if(a[i]>0)
            {
                maxx=max(maxx,a[i]);
            }
            else
            {
                sum+=maxx;
                maxx=0;
            }
        }
        ll maxx2=-inf;
        for(int i=0;i<n;i++)
        {
            if(a[i]<0)
            {
                maxx2=max(maxx2,a[i]);
            }
            else
            {
                if(maxx2!=-inf)
                    {
                        sum+=maxx2;
                    }            
                maxx2=-inf;
            }
        }
        if(a[n-1]>0)  //少加的情况。
            sum+=maxx;
        else
            sum+=maxx2;
        cout<<sum<<endl;
    }
    return 0;
}

     题意:给出偶数n个大小在[1,k]之间的数,操作是把任意一个数变成[1,k]之间的任意值。要达到每个ai+an-i+1都为定值x,求最少操作数。

     解析:我刚开始想的是,记录x出现的最大次数maxx,输出n-maxx,调了半天。原来我忽略了,ai和an-i+1,并不是随便改一个值就能使和达到[2,k]的任意值的。所以去分析每个x所对应的修改次数,下面开始讨论:

        sum=a[i]+a[n-i+1],maxx=max(a[i],a[n-i+1]),minn=min(a[i],a[n-i+1])

        1.如果x范围位于[2,minn],就算把maxx改成1,它们的和也是大于这个范围的,所以需要改动两次。

        2.如果x范围位于[minn+1,maxx+k],关于这个区间我解释一下:minn+1是把maxx变成1所得的和,maxx+k是把minn变成k所得的和,这是只改一个数所对应的和的范围。

       3.除了上面两种,就剩下[maxx+k+1,2*k]了。它和第一种一样,就算把minn变成k,它们的和也是小于这个范围的,所以要修改两个值。

      以上就需要差分数组维护了,关于差分数组,我有一篇博客可以参考:https://www.cnblogs.com/liyexin/p/11014218.html。对[L,R]每个数+1,就需要差分数组d[L]++,d[R+1]--。

      较为啰嗦的更新代码:

        for(int i=1;i<=n/2;i++)
        {
            ll sum=a[i]+a[n-i+1];
            ll minn=min(a[i],a[n-i+1]);
            ll maxx=max(a[i],a[n-i+1]);
            d[2]+=2;
            d[minn+1]-=2;
            d[minn+1]++;
            d[maxx+k+1]--;
            d[maxx+k+1]+=2;
            d[2*k+1]-=2;
            d[sum]--;
            d[sum+1]++;    //本来和就等于sum,所以不需要更新,需要--。
        }

      完整代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<queue>
typedef long long ll;
using namespace std;
const int maxn=2e5+10;
ll a[maxn],d[2*maxn];  //差分数组开两倍
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n , k;
        scanf("%d%d",&n,&k);
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        for(int i=1;i<=n/2;i++)
        {
            ll sum=a[i]+a[n-i+1];
            ll minn=min(a[i],a[n-i+1]);
            ll maxx=max(a[i],a[n-i+1]);
            d[2]+=2;
            d[minn+1]-=2;
            d[minn+1]++;
            d[maxx+k+1]--;
            d[maxx+k+1]+=2;
            d[2*k+1]-=2;
            d[sum]--;
            d[sum+1]++;
        }
        ll minn=d[2];
        for(int i=3;i<=2*k;i++)    //x范围就是[2,2*k]看看哪一个x所需要的更新次数最少?
        {
            d[i]+=d[i-1];
            minn=min(d[i],minn);
        }
        cout<<minn<<endl;
    }
}
原文地址:https://www.cnblogs.com/liyexin/p/12763687.html