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

先吐槽一下多校的难度
膜拜清华大神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;
}

总结:

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值