还是有点蠢。。。
多测没清空T3挂40。。。(只得了人口普查分20)
多测题要把样例复制粘两遍自测一下防止未清空出锅。
然而不算分。。。
其实到现在了算不算也不重要了吧。。。
而且其实T3只考虑最长路上的点这个思路其实肯定都能想到啊。
懒得打。那么就分少啊。
对于分数,还是要贪婪一些的。
T1:你相信引力吗
维护最大值关系:肉眼可见的单调栈/队列。(刚开始认为是栈,后来其实发现是一个队列)
环,经典套路,拆成两倍长的序列。
维护一个单调不增的单调栈,根据题目含义画画图,可以发现答案是:
对于当前点,从栈里第一个严格大于当前元素的值开始数,栈里有多少个元素。
暴力思路就是二分。
可以发现你找到的那个元素后面的部分都会被弹掉,所以一边弹一边计数即可。
特殊的一点是权值相同的,这部分不会被弹栈,但是对答案也有贡献。
所以在每个元素入栈的时候记录一下栈顶有几个连续相同的元素即可。
然后还要考虑找到第一个比当前元素大的元素,所以检查一下栈底的元素是否比当前元素大,如果大的话再累加1个答案。
然后还要考虑环拉成序列以后同一个位置的元素可能在栈里出现2次。
所以就不是栈了,是个队列。如果队首元素的下表就是当前的下标,那么就弹出。
弹出的时候要考虑它对“栈里连续相同元素数量”的影响。
还有一个问题,如果对于一对冰锥,它的优弧和劣弧都满足条件,那么它的答案会被计算两次。
如果最大值出现了k次,那么它对答案产生的多出的贡献就是$\frac{k(k-1)}{2}$
特别的,当k=1时,如果次大值出现了j次,那么多出的答案就是$j$
所以把多出的贡献减去就是最终答案。
细节较多。但是如果上述分类讨论都注意到了的话调试还是挺简单的。
1 #include<cstdio> 2 int read(){ 3 register int p=0;register char ch=getchar(); 4 while(ch<'0'||ch>'9')ch=getchar(); 5 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 6 return p; 7 } 8 int n,a[5000005],q[10000005],p[10000005],s[10000005],h=1,t,mx,se,tmx,tse;long long ans; 9 int main(){ 10 freopen("jolyne.in","r",stdin);freopen("jolyne.out","w",stdout); 11 n=read(); 12 for(int i=1;i<=n;++i)a[i]=read(); 13 for(int i=1;i<=n;++i) 14 if(a[i]>mx)se=mx,tse=tmx,mx=a[i],tmx=1; 15 else if(a[i]==mx)tmx++; 16 else if(a[i]>se)se=a[i],tse=1; 17 else if(a[i]==se)tse++; 18 for(int i=1;i<=n;++i){ 19 while(t>=h&&a[i]>q[t])t--; 20 if(t>=h&&q[t]==a[i])s[++t]=s[t-1]+1;else s[++t]=1; 21 q[t]=a[i];p[t]=i; 22 } 23 for(int i=1;i<=n;++i){int nt=0; 24 while(t>=h&&p[h]<=i)nt=a[i]==q[h],h++; 25 while(t>=h&&a[i]>q[t])t--,ans++; 26 if(t>=h&&q[t]==a[i])s[++t]=s[t-1]+1;else s[++t]=1; 27 q[t]=a[i];p[t]=n+i; 28 if(h==t||q[h]!=q[t])nt=0; 29 s[t]-=nt;ans+=s[t]-1;if(q[h]!=a[i])ans++; 30 // for(int j=h;j<=t;++j)printf("%3d ",q[j]);puts(""); 31 // for(int j=h;j<=t;++j)printf("%3d ",s[j]);puts(""); 32 // printf("%lld\n",ans); 33 } 34 if(tmx==1)ans-=tse; 35 else ans-=tmx*(tmx-1ll)/2; 36 printf("%lld\n",ans); 37 }