【AC梦工厂】2017 Multi-University Training Contest 3 1003|| HDU6058

来源:互联网 发布:gre单词软件 编辑:程序博客网 时间:2024/06/07 13:59

先吐槽一下多校的难度
膜拜清华大神AK

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6058

官方题解:

我们只要求出对于一个数x左边最近的k个比他大的和右边最近k个比他大的,扫一下就可以知道有几个区间的k大值是x.
我们考虑从小到大枚举x,每次维护一个链表,链表里只有>=x的数,那么往左往右找只要暴力跳k次,删除也是O(1)的。
时间复杂度:O(nk)

看!不!懂! 嗯,很容易理解嘛

题目大意:

给你一串1……n的排列需要你找出子串中第k小的元素的和,考虑到用链表去维护。求出对于一个数x左边最近的k个比他大的和右边最近k个比他大的,如果左边有m个比x大的就需要在右边找k-1-m个比x大的,统计这样的区间有多少个即可(呵呵,说得简单),再用区间的数量乘上x即x对答案的贡献

上代码:

#include <bits/stdc++.h>using namespace std;map<int,int>ma;                             /**记录每个元素的位置(当然也可以用数组减少复杂度)**/struct Link{    int pre,next,num;}s[500005];                                 /**用数组去模拟链表**/int main(){//    freopen("a.in","r",stdin);//    freopen("a.out","w",stdout);    int t;    scanf("%d",&t);    while(t--)    {        int n,k;        memset(s,0,sizeof s);        ma.clear();        long long sum=0;        scanf("%d %d",&n,&k);        for(int i=1;i<=n;i++)        {            scanf("%d",&s[i].num);            s[i].pre=i-1;                          /**前后指针**/            s[i].next=i+1;                         /**当n==i的时候不要把s[i].next设为0,不然结果可能会出现负数**/            ma[s[i].num]=i;        }        long long ans=0;        for(int i=1;i<=n;i++)        {            int l=ma[i],r=ma[i];            int cnt1=0,cnt2=0;            int x[100]={0};                       /**记录有多少个比x小的数字**/            for(int j=0;j<k;j++)                  /**往右找k个比x大的**/            {                if(s[r].next!=0)                {                    x[++cnt2]=s[r].next-r;                    r=s[r].next;                }else break;            }            long long res=0;            for(int j=l;j>0&&cnt1<k;j=s[j].pre)            {                cnt1++;                if(k-cnt1-cnt2+1>0) continue;                else res+=(j-s[j].pre)*x[k-cnt1+1];            }            ans+=res*i;            s[s[ma[i]].pre].next=s[ma[i]].next;          /**这个地方调了3个小时的bug 这里是模拟删除链表的操作**/            s[s[ma[i]].next].pre=s[ma[i]].pre;           /**怪自己的代码太丑**/        }        printf("%lld\n",ans);    }    return 0;}

附上标程:反正我看不懂

#include<bits/stdc++.h>#define fi first#define se second#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)#define per(i,j,k) for(int i=(int)j;i>=(int)k;i--)using namespace std;typedef long long LL;const int N=510000;inline void read(int &x){    x=0;char p=getchar();    while(!(p<='9'&&p>='0'))p=getchar();    while(p<='9'&&p>='0')x*=10,x+=p-48,p=getchar();}int n,k,a[N],pos[N],T;LL ans=0;int pre[N],np[N];int s[N],s0;int t[N],t0;void erase(int x){    int pp=pre[x];    int nn=np[x];    if(pre[x])np[pre[x]]=nn;    if(np[x]<=n)pre[np[x]]=pp;    pre[x]=np[x]=0;}void Main(){    read(n);read(k);    rep(i,1,n){        read(a[i]);        pos[a[i]]=i;    }    rep(i,1,n)pre[i]=np[i]=0;    rep(i,1,n)pre[i]=i-1,np[i]=i+1;    ans=0;    rep(num,1,n-k+1){        int p=pos[num];        s0=t0=0;        for(int d=p;d&&s0<=k+1;d=pre[d])s[++s0]=d;        for(int d=p;d!=n+1&&t0<=k+1;d=np[d])t[++t0]=d;        s[++s0]=0;        t[++t0]=n+1;        rep(i,1,s0-1){            if(k+1-i<=t0-1&&k+1-i>=1){                ans+=(t[k+1-i+1]-t[k+1-i])*1ll*(s[i]-s[i+1])*num;            }        }        erase(p);    }    cout<<ans<<endl;}int main(){    read(T);    while(T--)Main();    //cerr<<clock()<<endl;    return 0;}

总结:

这次的高校打的不是太好,其中之一是题目的难度有点高,但是这不能成为借口,与平时补题比较少有关,确实补题的需要花不少的时间,但确实是有必要的,同时专题的训练也是不能少的继续加油

阅读全文
0 0
原创粉丝点击