hdu 6058 思维

题目:http://acm.hdu.edu.cn/showproblem.php?pid=6058

分析题目的时候,由于枚举的区间很多,而第k大的值范围小,应该要想到去枚举第k大的值然后找到这个值对答案的贡献。

题解:我们只要求出对于一个数x左边最近的k个比他大的和右边最近k个比他大的,扫一下就可以知道有几个区间的kk大值是xx.(这个地方自己举几个例子就知道了)

我们考虑从小到大枚举x,每次维护一个链表(我写了一个双链表),链表里只有大于x的数,每次求x对答案的贡献的时候,直接在链表中x的位置左右k个值扫一边就可以了。

ac代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
struct node
{
    ll key;
    ll l,r;
}num[500001];
ll a[500001];
ll pos[500001];
ll pre[81],nex[81];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d %d",&n,&k);
        num[1].l=-1;
        num[n].r=-1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(i!=1) num[i].l=i-1;
            if(i!=n) num[i].r=i+1;
            num[i].key=a[i];
            pos[a[i]]=i;
        }
        ll sum=0;
        for(int i=1;i<=n;i++)
        {
            fill(pre+1,pre+k+1,-1);
            fill(nex+1,nex+k+1,-1);
            ll po=pos[i];
            ll temp=num[po].l;
            int ret=1;
            pre[0]=nex[0]=po;
            while(temp!=-1 && ret<=k)
            {
                pre[ret++]=temp;
                temp=num[temp].l;
            }
            temp=num[po].r;
            ret=1;
            while(temp!=-1 && ret<=k)
            {
                nex[ret++]=temp;
                temp=num[temp].r;
            }
            /*
            for(int i=1;i<=k;i++) cout<<pre[i]<<' ';
            cout<<endl;
            for(int i=1;i<=k;i++) cout<<nex[i]<<' ';
            cout<<endl;
            */
            for(int j=0;j<k;j++)
            {
                if(pre[k-j-1]==-1 || nex[j]==-1) continue;
                ll ans=pre[k-j-1]-pre[k-j];
                ll ans2=nex[j+1]-nex[j];
                if(pre[k-j]==-1) ans=pre[k-j-1];
                if(nex[j+1]==-1) ans2=n-nex[j]+1;
              //  cout<<ans<<' '<<ans2<<endl;
                sum+=ans*ans2*(ll)i;
            }
            // delete
            ll nl=num[po].l;
            ll nr=num[po].r;
            num[nl].r=nr;
            num[nr].l=nl;
        }
        printf("%lld\n",sum);
    }
    return 0;
}

用链表去维护左右比x大的数还是很神奇的(从小到大去计算贡献值,每次算完小的值就从链表里面删去,,,, 厉害了)。。这个真心没想到

posted @ 2017-08-03 20:00  猪突猛进!!!  阅读(278)  评论(0编辑  收藏  举报