先吐槽一下多校的难度
膜拜清华大神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;
}
总结:
这次的高校打的不是太好,其中之一是题目的难度有点高,但是这不能成为借口,与平时补题比较少有关,确实补题的需要花不少的时间,但确实是有必要的,同时专题的训练也是不能少的继续加油
380

被折叠的 条评论
为什么被折叠?



