南昌邀请赛网络预选赛 部分题解

I Max answer

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input

First line contains an integer n(1≤n≤5×10^5).

Second line contains nn integers represent the array a (-10^5<=a<=10^5).

Output

One line contains an integer represent the answer of the array.

样例输入复制

5
1 2 3 4 5

样例输出复制

36

求 一个区间内的最小值乘这个区间和所得到的数 最大

如果全是整数的话,可以用单调栈,以h[i]为点(把h[i]看作是最小的)往左右两边扩展

但这里存在负数,例如 -8 -7 1 -8 -7 所得结果最大是232,区间是[-8,-7,1,-8,-7],如果单纯用单调栈的话会有正负变小的情况

所以负数的时候需要另外判断。

由于正数区间不能包括负数,否则会变成负的(比如 8 7 -1 8 7 最小的是-1,结果变成-22),但是负数区间可以包含正数(比如-8 -7 1 -8 -7,负负得正变成232),所以当h[i]为正数的只用单调栈判断即可,当h[i]为负数的时候,就需要从L[i]到R[i]计算大小

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+5;
int n;
ll h[maxn];
ll sum[maxn];
int L[maxn],R[maxn];
ll maxL[maxn];
stack<int> mystack;

ll Solve()
{
    while(!mystack.empty())
        mystack.pop();

    for(int i=1;i<=n;i++)
    {
        while(!mystack.empty()&&h[mystack.top()]>=h[i])//直到栈顶元素<h[i],才到左边界
            mystack.pop();
        L[i]=mystack.empty()?1:mystack.top()+1;//左边界

        mystack.push(i);
    }

    while(!mystack.empty())
        mystack.pop();

    for(int i=n;i>=1;i--)
    {
        while(!mystack.empty()&&h[mystack.top()]>=h[i])//直到栈顶元素<h[i],才到右边界
            mystack.pop();
        R[i]=mystack.empty()?n:mystack.top()-1;

        mystack.push(i);
    }

    for(int i=1;i<=n;i++)//左边 从L[i]到i位置
    {
        if(h[i]>=0)
            continue;
        for(int j=L[i];j<=i;j++)
            maxL[i]=min(maxL[i],sum[i]-sum[j-1]);
    }

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        if(h[i]>=0)
        {
            ans=max(ans,h[i]*(sum[R[i]]-sum[L[i]-1]));
            continue;
        }
        for(int j=i;j<=R[i];j++)
            ans=max(ans,h[i]*(maxL[i]+(sum[j]-sum[i])));//右边 左边最大的+从i位置到R[i]
    }
    return ans;
}


int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&h[i]);
        sum[i]=sum[i-1]+h[i];
    }
    printf("%lld\n",Solve());
}

K MORE XOR

Given a sequence of nn numbers a1, a2, cdots, an,a1​,a2​,⋯,an​ and three functions.

Define a function f(l,r) which returns plus a[x]⊕a[x] (l<=x,y<=r)). The plus⊕ represents exclusive OR.

Define a function g(l,r) which returns plus f(x,y) (l<=x,y<=r)⊕f(x,y)(l≤x≤y≤r).

Define a function w(l,r) which returns plus g(x,y) (l<=x,y<=r)⊕g(x,y)(l≤x≤y≤r).

You are also given a number of xor-queries. A xor-query is a pair (i, j) (1≤i≤j≤n). For each xor-query (i,j), you have to answer the result of function w(l,r)

Input

Line 1:t (1≤t≤20).

For each test case:

Line 1: n(1≤n≤100000).

Line 2: n numbers a1​,a2​,⋯,an​(1≤ai​≤109).

Line 3: q (1≤q≤100000), the number of xor-queries.

In the next q lines, each line contains 2 numbers i, j representing a xor-query(1≤i≤j≤n).

It is guaranteed that sum of n and q 10^6.

Output

For each xor-query (i, j), print the result of function w(i,j) in a single line.

样例输入复制

1
5
1 2 3 4 5
5
1 3
1 5
1 4
4 5
3 5

样例输出复制

2
4
0
1
4

函数调用有点绕,用一个例子来说明

w(1,2)=g(1,1)^g(1,2)^g(2,2)

g(1,1)=f(1,1)

g(1,2) = f(1,1) ^ f(1,2) ^ f(2,2)

g(2,2)=f(2,2)

f(1,1)=a[1]

f(1,2) = a[1]^a[2] 

f(2,2)=a[2]

这样表述大概就很清晰明了了。

我不知道别人是怎么推出来的公式,反正我是非常非常暴力,手动打表的方式(哭唧唧,我可以说推这个题的公式推了好久好久嘛,过题的人真的太神仙了叭

为这个题贡献了四个程序,先打表模拟,找出大体规律

#include<bits/stdc++.h>
using namespace std;
int a[100];

int f(int l,int r)
{
    int ans=0;
    for(int i=l;i<=r;i++)
        ans^=a[i];

    return ans;
}

int g(int l,int r)
{
    int ans=0;
    for(int i=l;i<=r;i++)
    {
        for(int j=i;j<=r;j++)
        {
//            cout<<"f(i,j) "<<"i:"<<i<<" j:"<<j<<"  "<<f(i,j)<<endl;
            ans^=f(i,j);
        }
    }
    return ans;
}

int w(int l,int r)
{
    int ans=0;
    for(int i=l;i<=r;i++)
    {
        for(int j=i;j<=r;j++)
        {
//            cout<<"g(i,j) "<<"i:"<<i<<" j:"<<j<<"  "<<g(i,j)<<endl;
            ans^=g(i,j);
        }
    }
    return ans;
}

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];

    int ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            ans^=w(i,j);
            //cout<<ans<<endl;
            cout<<"w(i,j) "<<"i:"<<i<<" j:"<<j<<"  "<<w(i,j)<<endl;
        }
    }
}

异或有一个性质 偶数个相同的数异或起来为0,我就是像例子中这样例如f(1,1)和f(1,1)可以异或为0,可以消掉。手动消消乐(并不快乐!算了很多组数据,得出

 

4个数为一个节  

(j-i+1)mod 4==0,w(i,j)=0

(j-i+1)mod 4==1,w(i,j)= 在第1个位置上的数异或起来

(j-i+1)mod 4==2,w(i,j)=在第1和2个位置上的数异或起来

(j-i+1)mod 4==3,w(i,j)=在第2个位置上的数异或起来

然后我就for循环去找惹,呜呜呜还是太年轻,果真tle炸了

第二个开始往正确方向靠的思路,不过 T 炸了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+5;
int n,m;
int a[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        scanf("%d",&m);
        int l,r;
        while(m--)
        {
            ll ans=0;
            scanf("%d%d",&l,&r);
            if((r-l+1)%4==0)
                ans=0;
            else if((r-l+1)%4==1)
            {
                for(int i=l;i<=r;i+=4)
                {
                    ans^=a[i];
//                    cout<<"i:"<<i<<endl;
                }
            }
            else if((r-l+1)%4==2)
            {
                for(int i=l;i+1<=r;i+=4)
                {
                    ans^=(a[i]^a[i+1]);
//                    cout<<"i:"<<i<<" i+1:"<<i+1<<endl;
                }
            }
            else if((r-l+1)%4==3)
            {
                for(int i=l;i+1<=r;i+=4)
                {
                    ans^=a[i+1];
//                    cout<<"i+1:"<<i+1<<endl;
                }
            }
            printf("%lld\n",ans);
        }
    }
}

跟李先生说了一下思路,他示意窝往前缀和的方向去想

sum数组代表前缀异或,一组间隔为4(因为上面找出来的规律是%4

1,5,9,11...

2,6,10,12...

3,7,11,13...

4,8,12,16...

然后无限打表

我找规律的方法貌似很笨,分别找出几组余数为1,2,3的数,根据第二个程序看他所需要的异或的数是什么,然后在sum数组里找规律

例如 余数为1

(1-1)+1……(1) …… sum[1]

(5-1)+1……(1^5)……sum[5]

(9-1)+1……(1^5^9)……sum[9]

(6-2)+1……(2^6)……sum[6]

(10-2)+1……(2^6^10)……sum[10]

(7-3)+1……(3^7)……sum[7]

(11-3)+1……(3^7^11)……sum[11]

(12-4)+1……(4^8^12)……sum[12]

(13-5)+1……(5^9^13)……sum[13]^sum[1]

(14-6)+1……(6^10^14)……sum[14]^sum[2]

(10-6)+1……(6^10)……sum[10]^sum[2]

(11-7)+1……(7^11)……sum[11]^sum[3]

然后因为%4==2的情况比较麻烦,所以在1,3推出公式后就交了一发,然后果断AC,哇,太幸福了吧,%4==2的数据就这样水过去了???


#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+5;
int n,m;
int a[maxn];
ll sum[maxn];//前缀异或 1 2 3 4
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&sum[i]);
            a[i]=sum[i];
            if(i>4)
                sum[i]^=sum[i-4];
        }

        scanf("%d",&m);
        int l,r;
        while(m--)
        {
            ll ans=0;
            scanf("%d%d",&l,&r);
            if((r-l+1)%4==0)
                ans=0;
            else if((r-l+1)%4==1)
            {
                if(l>=4)
                    ans=sum[r]^sum[l-4];
                else
                    ans=sum[r];
            }
            else if((r-l+1)%4==2)
            {
                for(int i=l;i+1<=r;i+=4)
                    ans^=(a[i]^a[i+1]);
            }
            else if((r-l+1)%4==3)
            {
                if(l>=3)
                    ans=sum[r-1]^sum[l-3];
                else
                    ans=sum[r-1];
            }
            printf("%lld\n",ans);
        }
    }
}

存有侥幸心理 我能甘心??不能!于是继续打表终于完全搞出来了


#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+5;
int n,m;
ll sum[maxn];//前缀异或 1 2 3 4
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&sum[i]);
            if(i>4)
                sum[i]^=sum[i-4];
        }

        scanf("%d",&m);
        int l,r;
        while(m--)
        {
            ll ans=0;
            scanf("%d%d",&l,&r);
            if((r-l+1)%4==0)
                ans=0;
            else if((r-l+1)%4==1)
            {
                if(l>=4)
                    ans=sum[r]^sum[l-4];
                else
                    ans=sum[r];
            }
            else if((r-l+1)%4==2)
            {
                ans^=sum[r]^sum[r-1];
                if(l>=3)
                    ans^=sum[l-3];
                if(l>=4)
                    ans^=sum[l-4];
            }
            else if((r-l+1)%4==3)
            {
                if(l>=3)
                    ans=sum[r-1]^sum[l-3];
                else
                    ans=sum[r-1];
            }
            printf("%lld\n",ans);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值