2013 ACM/ICPC Asia Regional Changsha Online - J

原题戳这里

题意: 有一未知列数a1,a2,a3.....an,

    已知s[i]=a[i-1]+a[i]+a[i]  (1<i<n)

                  s[1]=a[1]+a[2];

      s[n]=a[n-1]+a[n]

         还知道a数列中一些数的值(可以全部不知道)

   询问一些数 可能的最大值是多少。

 

 

首先,可以根据s数组求出a[i]的值(i为3 的倍数) answer[3k]=s[3k-1]-s[3k-2]+a[3k-3];

其次,要是知道连续两个数a[i] ,a[i+1],或者a[i],a[i+1],那么就能确定整个序列。,问题解决,直接回复 询问即可。

最后,要是不能确定整个序列,那么所有a[i]都是未知的(i不为3 的倍数)。下面求这些未知a[i]的可能最大值。

     求a中i=3k+1位上的最大值

           1.设a[1]=s[1],则a[2]=0;

           2.根据a[i]=s[i-1]-a[i-1]-a[i-2],暂时求出一个可能非法(出现负值)的序列temp。

          3.找到i=3k+2位上的最小值min(min<=0) (min必不大于0是因为a[2]=0)

          4.为了让a[3k+2]上的数合法,把temp[3k+1] 的数降低-min(min为负数),temp[3k+1]的数上升-min。

    这样temp就合法,且temp[3k+1]为最大,temp[3k+2]为最小,取temp[3k+1]作为3k+1位上的答案。

             answer[3k+1]=temp[3k+1]+min;

    求a中i=3k+2位上的最大值

           1.设a[1]=0;a[2]=s[1];

           2.上同

           3.找到i=3k+1位上的最小值min

           4.answer[3k+2]=temp[3k+2]+min;

    一个answer数组就能离线地处理这些询问了。

        PWW学姐问这么算出来为什么是最大而且合法的,当初给她的解释是在求i=3k+1时,先把temp[3k+1]做成最大,temp[3k+2]略显非法,(这时temp[3k+1]肯定是最大的),同时把temp[3k+1]做最小地牺牲(即降低一个值),使得temp[3k+2]也是合法的。同时这个牺牲显然也是最小的。所以temp[3k+1]经过调整后肯定是最大的。因为是最大的,所以temp[3k+1]肯定是非负的,否则肯定是数据错了。

        当然这样是毛估估的。。。(懂“毛估估”这个词么?就是在简单得在心里蹭蹭。“蹭蹭”:=“想一想”)

        证明最大性质的证明过程:

             假设在3k+1这个位置的最大值不是temp[3k+1] ,而是m,则设dis=m-temp[3k+1] (dis>0)。

              此时在3k+2这个位置的值应该为temp[3k+2]-dis   (temp[3k+1]+temp[3k+2] 为定值)

              同理3k+4,这个位置的值应该为temp[3k+2]+dis  (temp[3k+2]+temp[3k+4]为定值)

              即每个位置的值是temp中所有3k+1位加dis,3k+2位减dis.  

              而temp[3k+2]至少有一个数为0(算法流程里体现了),这是这个位置的数为-dis,负数,矛盾,非法,不成立。

     证毕。

     

     

 

 

 

 

 

      PS:看到我的两个小队友的blog了,感觉略感落伍,以前以为大牛才能有blog。谨以此文,纪念我的第一篇公开解题报告。

             代码略难看。。。(羞涩)

#include <stdio.h>
#include <string.h>
#define MAXN 100005
#define max(a,b) a>b? a:b
int n,m;
int tell[MAXN],s[MAXN];
int query[1000];
int a[MAXN];
int ansmax[MAXN],ansmin[MAXN];
int temp[MAXN];
void fuck() //这种情况最恶心了,比赛就是写这种情况来不及了。。诅咒之
{
    temp[1]=s[1];// 3k+1位最大
    temp[2]=0;
    int i;
    for (i =3; i<=n; i++)
        temp[i]=s[i-1]-temp[i-1]-temp[i-2];
    int min=0x7ffffff;
    for (i=2; i<=n; i=i+3)
        if (min > temp[i])
            min = temp[i];
    for (i=1; i<=n; i=i+3)
        ansmax[i]=temp[i]+min;
    temp[n]=s[n];//3k+2位最大
    temp[n-1]=0;
    for (i =n-2; i>=1; i--)
        temp[i]=s[i+1]-temp[i+1]-temp[i+2];
    min=0x7ffffff;
    for (i=1; i<=n; i=i+3)
        if (min > temp[i])
            min = temp[i];
    for (i=2; i<=n; i=i+3)
        ansmax[i]=temp[i]+min;
    for (i = 3; i<n; i=i+3)
        ansmax[i]=temp[i];
    ansmax[n]=max(ansmax[n],temp[n]+min);
    for (i=1; i<=m; i++)
        printf("%d\n",ansmax[query[i]+1]);
}
void work()
{
    int i=2,j=3,k;
    memset(a,-1,sizeof(a));
    a[0]=0;
    while (j<=n) //顺序3k位置的值
    {
        a[j] = s[j-1]-s[j-2]+a[j-3];
        j+=3;
    }
    j = n-2;
    a[n+1]=0;
    while ( j>=1)  //逆序3k位置的值
    {
        a[j] = s[j+1]-s[j+2] +a[j+3];
        j-=3;
    }
    for (i=1; i<=n; i++)
        if (tell[i] != -1)
            a[i]=tell[i];
    int flag=true;
    printf("\n");
    for (i=1 ;i+2 <= n; i++)  //a[i],a[i+2]已知,求a[i+1]
        if ( a[i] != -1 && a[i+2] != -1)
            a[i+1]=s[i+1]-a[i]-a[i+2];
    for ( i = 1; i <n && flag; i++)//检测是否有连续两个数存在的情况
        if (a[i]!= -1 && a[i+1] !=-1)
        {
            flag=false;
            j=i;
            for (k=i-1; k>=1; k--)
                a[k] =s[k+1]-a[k+1]-a[k+2];
            for (k=i+2; k<=n; k++)
                a[k]= s[k-1]-a[k-1]-a[k-2];
        }
        if (!flag) //如果有就ok了
         for (k=1; k<=m; k++)
                printf("%d\n",a[query[k]+1]);
    if ( flag )//没有连续两个数。
        fuck();
int main()
{
    int i,j;
    while (scanf("%d",&n)!=EOF)
    {
        for (i=1; i<=n; i++)
            scanf("%d",&tell[i]);
        for (i=1; i<=n; i++)
            scanf("%d",&s[i]);
        scanf("%d",&m);
        for (i=1; i <=m; i++)
            scanf("%d",&query[i]);
        work();
    }
    return 0;
}

 

posted on 2013-09-24 16:49  six_god  阅读(214)  评论(0编辑  收藏  举报

导航