当前位置:   article > 正文

题解 CF283E 【Cow Tennis Tournament】

cf283e cow tennis tournament

题目大意

给出n个正整数,由大小确定数与数之间的指向关系(大的指向小的),其中大小处在某一区间的数指向关系全部取反,问n个数之间的三元环个数

正在做学长推荐的数据结构题,莫名看到了这道数学(蒟蒻感觉对这种有数学成分的题相当无力啊),于是本蒟蒻想(kan ti jie)了好久好久,终于受到学长数学+数据结构的真传切了这题(先orz切题的大佬一波)

具体思路

first

不管怎样,si数值范围很大,而区间取反也是从数值入手,既然如此,不管三七二十一,先把数值离散化一波,用数值大小的排名代替数值,将数值范围化为1n,然后把要取反的区间两端做同样的操作。又因为反正是数值区间上的取反,干脆把所有数再排下序,这样基本操作就完成了。

second

三元环不好直接求,,终于发现可以 容斥 一波,先求出所有方案:

Cn3

然后是不符合题意的方案数,本蒟蒻觉得分为两种:

  1. 一群牛之间互相都不可以击败对方(即一堆相等的正整数),这样的一堆数之间没有指向性。这样的话,我们可以用一个数组cntx,来记录数x出现的个数,而明显看得出来,只要在每堆这些相等的数中任意取2个,则方案不合理。综上,该情况的不合理方案为(式子里的si为离散化处理过的si数组):
    i=s1snCcnti2

  2. 一头牛很勇,可以搞其他很多头牛(即一个数很nb,可以指(cha)向其他的很多个数)。而一个数x能指向的数有且仅有两种情况(又是两种……) :a.一个数y比x小且未被经过x的区间经过奇数次(即未被经过x的区间给d掉);b.一个数z比x大且被经过x的区间经过奇数次(即被经过x的区间给d掉)。设第一种情况存在可能的y有res1种,第二种情况存在可能的z有res2种,那么显然,在res1res2中取出任两个和x搭配都会导致方案不合理。设res=res1+res2,则不合理方案数为: Cres2。而总的不合理方案书如下:
    i=s1snCresi2

third

由上总结后,求解答案的思路清晰很多了呢(没有的话别喷蒟蒻)。但看看上面我们要求的两个东西,Ccnti2很好求,但那个很勇的牛的情况就有点%¥&……

关键是经过x的区间要算、而没经过的不能算进去这个不好处理,莫非一个个数值暴力枚举??这样肯定超时,怎么办呢??

本蒟蒻脑子转得慢,搞不懂怎么处理,去问学长,学长说要用扫描线,我说我不会,于是学长耐心地跟我讲了扫描线,我认真滴听,然后……没听懂。

关于学长讲的扫描线,我只记得好像是先按区间左界sort一波,再操作一波,再按右界sort一波,再操作一波,然后就有答案了……

但我不会操作啊!!

算了,按照学长的左界sort先yy一波,灵光一闪!从s1开始遍历到sn,每循环到一个点i就把左界小于i的还未取反的区间给进行取反,

然后把区间左界l压入以右界r为关键词的一个动态数组(vectore[r],然后再把e[i1]里的左界一一取出,再进行一次取反(两次取反便为还原,就相当于把区间给排除了)。

forth

具体的区间取反使用线段树实现,这样我们就很愉快地切题了

时间复杂度O(L+klogL)L为s中不同数值的个数, 感觉应该比O(n+klogn)的期望要好些

fifth

祝大家切题愉快

sixth

附上代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<vector>
  5. using namespace std;
  6. #define in read()
  7. #define fur(i,a,b) for(int i=a;i<=b;i++)
  8. #define xx 110000
  9. #define int long long
  10. #define ll long long
  11. inline int read()
  12. {
  13. int x=0,f=1;char ch=getchar();
  14. for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
  15. for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
  16. return x*f;
  17. }
  18. int s2[xx],s1[xx],s[xx],cnt[xx]; //s存战斗力,s2存排序后的,s1存s2去重后的,再将s1里的排名赋到s上
  19. int n,c[4][xx],all=0; //c存组合数(当然也可以在线算),all记录s2长度
  20. struct prom{int a,b;}t[xx]; //存储区间
  21. struct linetree{int l,r,tot,sum;bool tag;}lt[xx<<2]; //tot存储总数,sum存储被取反数
  22. inline bool cmp(prom x,prom y){return x.a<y.a;}
  23. vector<int>e[xx];
  24. inline int look2(int x) //取出s1中第一个小于等于x的数的下标,即排名
  25. {
  26. int head=1,tail=all,ans=1;
  27. while(head<=tail)
  28. {
  29. int mid=(head+tail)>>1;
  30. if(s1[mid]<=x) ans=mid,head=mid+1;
  31. else tail=mid-1;
  32. }
  33. return ans;
  34. }
  35. inline int look1(int x) //取出s1中第一个大于等于x的数
  36. {
  37. int head=1,tail=all,ans=all;
  38. while(head<=tail)
  39. {
  40. int mid=(head+tail)>>1;
  41. if(s1[mid]>=x) ans=mid,tail=mid-1;
  42. else head=mid+1;
  43. }
  44. return ans;
  45. }
  46. //------------
  47. //线段树
  48. inline void up(int k)
  49. {
  50. int q=k<<1;
  51. lt[k].sum=lt[q].sum+lt[q+1].sum;
  52. lt[k].tot=lt[q].tot+lt[q+1].tot;
  53. }
  54. inline void down(int k)
  55. {
  56. if(lt[k].tag)
  57. {
  58. int q=k<<1;
  59. lt[q].sum=lt[q].tot-lt[q].sum;
  60. lt[q+1].sum=lt[q+1].tot-lt[q+1].sum;
  61. lt[q].tag^=1;
  62. lt[q+1].tag^=1;
  63. lt[k].tag^=1;
  64. }
  65. }
  66. inline void build(int k,int i,int j)
  67. {
  68. lt[k].l=i;
  69. lt[k].r=j;
  70. if(i==j)
  71. {
  72. lt[k].tot=cnt[i];
  73. return;
  74. }
  75. int q=k<<1,mid=(i+j)>>1;
  76. build(q,i,mid);
  77. build(q+1,mid+1,j);
  78. up(k);
  79. }
  80. inline void change(int k,int i,int j)
  81. {
  82. if(i<=lt[k].l&&j>=lt[k].r)
  83. {
  84. lt[k].sum=lt[k].tot-lt[k].sum;
  85. lt[k].tag^=1;
  86. return;
  87. }
  88. down(k);
  89. int q=k<<1,mid=lt[q].r;
  90. if(i<=mid) change(q,i,j);
  91. if(j>mid) change(q+1,i,j);
  92. up(k);
  93. }
  94. inline int askus(int k,int i,int j) //访问区间中没有被取反的数的个数
  95. {
  96. if(i>j) return 0;
  97. if(i<=lt[k].l&&j>=lt[k].r) return lt[k].tot-lt[k].sum;
  98. down(k);
  99. int q=k<<1,mid=lt[q].r,res=0;
  100. if(i<=mid) res+=askus(q,i,j);
  101. if(j>mid) res+=askus(q+1,i,j);
  102. return res;
  103. }
  104. inline int askrs(int k,int i,int j) //访问区间中被取反的数的个数
  105. {
  106. if(i>j) return 0;
  107. if(i<=lt[k].l&&j>=lt[k].r) return lt[k].sum;
  108. down(k);
  109. int q=k<<1,mid=lt[q].r,res=0;
  110. if(i<=mid) res+=askrs(q,i,j);
  111. if(j>mid) res+=askrs(q+1,i,j);
  112. return res;
  113. }
  114. //线段树
  115. //------------
  116. signed main()
  117. {
  118. n=in;
  119. fur(i,1,n)
  120. {
  121. c[0][i]=1;c[1][i]=i;
  122. fur(j,2,min(i,3ll)) c[j][i]=c[j-1][i-1]+c[j][i-1];
  123. }
  124. int k=in;
  125. fur(i,1,n) s2[i]=s1[i]=s[i]=in;
  126. sort(s2+1,s2+n+1);
  127. fur(i,1,n)
  128. {
  129. while(s2[i]==s2[i+1]) i++;
  130. s1[++all]=s2[i];
  131. }
  132. fur(i,1,n) s[i]=look1(s[i]),cnt[s[i]]++;
  133. sort(s+1,s+n+1);
  134. build(1,1,s[n]);
  135. fur(i,1,k) t[i].a=in,t[i].b=in;
  136. fur(i,1,k) t[i].a=look1(t[i].a),t[i].b=look2(t[i].b);
  137. sort(t+1,t+k+1,cmp);
  138. int ans=c[3][n],tnt=1;
  139. fur(i,1,s[n])
  140. {
  141. while(t[tnt].a<=i&&tnt<=k) //先算区间左界小于等于当前i的
  142. {
  143. change(1,t[tnt].a,t[tnt].b);
  144. e[t[tnt].b].push_back(t[tnt].a);
  145. tnt++;
  146. }
  147. fur(j,0,(int)e[i-1].size()-1) change(1,e[i-1][j],i-1ll); //再删区间右界小于当前i的
  148. int res=0;
  149. res+=askus(1,1,i-1);
  150. res+=askrs(1,i+1,s[n]);
  151. ans-=cnt[i]*c[2][res];
  152. ans-=c[2][cnt[i]];
  153. }
  154. printf("%lld\n",ans);
  155. return 0;
  156. }

转载于:https://www.cnblogs.com/ALANALLEN21LOVE28/p/11312955.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/64638
推荐阅读
相关标签
  

闽ICP备14008679号