zoukankan      html  css  js  c++  java
  • 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
    ",sum);
        }
        return 0;
    }

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

  • 相关阅读:
    Android设置RadioButton在文字的右边
    如何创建启动界面Splash Screen
    sqlite3 数据类型 批量插入
    为PopupWindow设置弹出动画效果
    android activity生命周期
    Eclipse快捷键大全
    SQLite的使用
    创建窗口式Activity
    Android中实现按钮自动隐藏
    android技术片段
  • 原文地址:https://www.cnblogs.com/z1141000271/p/7281630.html
Copyright © 2011-2022 走看看