题解

老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很

现在后缀树上线段树合并差点把我写死

主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了

但就是不想写

但是由于我过于老年化,我还是决定记录一下我的思路

我用后缀自动机建的后缀树,所以是反串的后缀树,我考虑的都是区间字符串的结束位置

先熟练的建一个后缀树,我们对于每个区间代表的字符串,可以在后缀树上找到对应的节点,这个节点的子树里包含的结束位置往前数\(R - L + 1\)就是所有的这个区间字符串出现的位置

我们考虑反着来,好容易啊只需要统计三个区间都没有的方案呗!

30min过去了。。。咋统计啊= =

好吧,反着失败了,我们试着正着来,正着需要7个值

+至少在左边有

+至少在中间有

+至少在右边有

-至少在左边和中间有

-至少在左边和右边有

-至少在中间和右边有

+三个都有

容斥原理嘛。。

似乎可以维护了嘛

设区间长为\(l = R - L + 1\)

对于至少在左边有的

我们需要找到结束位置最小的地方\(t\),\(i >= t\)的就是合法区间

对于至少在右边有的

我们需要找到结束位置最大的地方\(t\),\(j <= t - l + 1\)的就是合法区间

对于至少在中间有的

考虑从两个相邻的位置\(b,a\),\(a >= b\)

然后他们产生的贡献是\((a - b) * (b - l)\),拆开就是\((a - b) * b - l *(a - b)\)

所以维护一个相邻位置的\((a - b) * b\)

最后减去用区间最大值减最小值乘上\(l\)

对于至少在左边和中间边有的

设\(a\)为出现最靠前的结束位置

也是两个相邻位置\(c,b\),\(b >= c\)

要求\(c - l >= a\)

贡献就是\((b - c) * (N - b)\)

那就再维护一个\((b - c) * b\)好了

至少在左边和右边有的

\(a\)为最靠前的结束位置,\(b\)为最靠后的结束位置 - l + 1

然后求左端点大于等于\(a\)

右端点小于等于\(b\)的方案数

至少在右边和中间有的

\(a\)为最靠后的结束位置-l + 1

相邻位置\(c,b\),\(b >= c\)

要求是\(b < a\)

然后统计起来是\((b - c) * (c - l)\)

维护\((b - c) * c\)事实上这是我们考虑只有中间有的时候维护的东西

三个都有的

\(a\)为最靠前的结束位置,\(b\)为最靠后的结束位置 - l + 1

一个结束位置\(c\)必须\(a + l <= c <= b - 1\)

然后相邻位置\(c,d\),有\(c >= d\)时

统计是\((c - d) * (d - l - a + 1)\)也是和考虑只有中间有维护的东西一样

维护的方式就是记录区间最大最小,合并左右区间的时候考虑中间两个点新的贡献

后五种情况都要算一下边界的区间的方案

然后就变成了,我们离线所有询问,把询问挂在节点上,然后dfs的时候合并线段树,就顺带处理了这个节点上所有询问的答案

为啥我的代码又是别人的2倍????8.3K了解一下????

实在是码农啊,服气服气,考场上给我五个点我也写不完,老了= =

代码

  1. #include <bits/stdc++.h>
  2. #define fi first
  3. #define se second
  4. #define pii pair<int,int>
  5. #define mp make_pair
  6. #define pb push_back
  7. #define enter putchar('\n')
  8. #define space putchar(' ')
  9. #define MAXN 200005
  10. #define eps 1e-8
  11. //#define ivorysi
  12. using namespace std;
  13. typedef long long int64;
  14. typedef double db;
  15. template<class T>
  16. void read(T &res) {
  17. res = 0;char c = getchar();T f = 1;
  18. while(c < '0' || c > '9') {
  19. if(c == '-') f = -1;
  20. c = getchar();
  21. }
  22. while(c >= '0' && c <= '9') {
  23. res = res * 10 + c - '0';
  24. c = getchar();
  25. }
  26. res *= f;
  27. }
  28. template<class T>
  29. void out(T x) {
  30. if(x < 0) {x = -x;putchar('-');}
  31. if(x >= 10) {
  32. out(x / 10);
  33. }
  34. putchar('0' + x % 10);
  35. }
  36. int N,Q;
  37. int64 ans[300005];
  38. char s[MAXN];
  39. vector<pii > R[MAXN],qry[MAXN];
  40. namespace SegmentTree {
  41. struct node {
  42. int lc,rc;
  43. int minv,maxv;
  44. int64 sum[2];
  45. }tr[MAXN * 20];
  46. int rt[MAXN],Ncnt;
  47. void Init() {
  48. tr[0].minv = N + 1,tr[0].maxv = 0,tr[0].sum[0] = tr[0].sum[1] = 0;
  49. }
  50. void update(int u) {
  51. tr[u].minv = min(tr[tr[u].lc].minv,tr[tr[u].rc].minv);
  52. tr[u].maxv = max(tr[tr[u].lc].maxv,tr[tr[u].rc].maxv);
  53. for(int i = 0 ; i <= 1 ; ++i) tr[u].sum[i] = tr[tr[u].lc].sum[i] + tr[tr[u].rc].sum[i];
  54. if(tr[u].lc && tr[u].rc) {
  55. int lm = tr[tr[u].lc].maxv,rm = tr[tr[u].rc].minv;
  56. if(lm <= rm) {
  57. tr[u].sum[0] += 1LL * (rm - lm) * lm;
  58. tr[u].sum[1] += 1LL * (rm - lm) * rm;
  59. }
  60. }
  61. }
  62. int Merge(int u,int v,int L,int R) {
  63. if(!u) return v;
  64. if(!v) return u;
  65. int mid = (L + R) >> 1;
  66. tr[u].lc = Merge(tr[u].lc,tr[v].lc,L,mid);
  67. tr[u].rc = Merge(tr[u].rc,tr[v].rc,mid + 1,R);
  68. update(u);
  69. return u;
  70. }
  71. void Insert(int &u,int p,int L,int R) {
  72. if(!u) {u = ++Ncnt;}
  73. if(L == R) {
  74. tr[u].minv = tr[u].maxv = p;tr[u].sum[0] = tr[u].sum[1] = 0;
  75. return;
  76. }
  77. int mid = (L + R) >> 1;
  78. if(p <= mid) Insert(tr[u].lc,p,L,mid);
  79. else Insert(tr[u].rc,p,mid + 1,R);
  80. update(u);
  81. }
  82. int64 Query(int u,int ql,int qr,int L,int R,int id,int &v,pii &rg) {
  83. if(!u) return 0;
  84. if(L == ql && R == qr) {
  85. rg.fi = min(rg.fi,tr[u].minv);
  86. rg.se = max(rg.se,tr[u].maxv);
  87. int64 res = tr[u].sum[id];
  88. if(!id) {
  89. if(v <= tr[u].minv) {
  90. res += 1LL * (tr[u].minv - v) * v;
  91. }
  92. if(tr[u].maxv <= N) v = tr[u].maxv;
  93. }
  94. else {
  95. if(v >= tr[u].maxv) {
  96. res += 1LL * (v - tr[u].maxv) * v;
  97. }
  98. if(tr[u].minv >= 1) v = tr[u].minv;
  99. }
  100. return res;
  101. }
  102. int mid = (L + R) >> 1;
  103. if(qr <= mid) return Query(tr[u].lc,ql,qr,L,mid,id,v,rg);
  104. else if(ql > mid) return Query(tr[u].rc,ql,qr,mid + 1,R,id,v,rg);
  105. else {
  106. if(!id) return Query(tr[u].lc,ql,mid,L,mid,id,v,rg) + Query(tr[u].rc,mid + 1,qr,mid + 1,R,id,v,rg);
  107. else return Query(tr[u].rc,mid + 1,qr,mid + 1,R,id,v,rg) + Query(tr[u].lc,ql,mid,L,mid,id,v,rg);
  108. }
  109. }
  110. }
  111. using SegmentTree::rt;
  112. using SegmentTree::tr;
  113. using SegmentTree::Query;
  114. using SegmentTree::Insert;
  115. using SegmentTree::Merge;
  116. namespace SuffixTree {
  117. struct node {
  118. int to,next,len;
  119. }E[MAXN * 4];
  120. int head[MAXN],sumE,ed[MAXN],dis[MAXN],fa[MAXN][20],Ncnt;
  121. void add(int u,int v,int c) {
  122. E[++sumE].to = v;
  123. E[sumE].next = head[u];
  124. E[sumE].len = c;
  125. head[u] = sumE;
  126. }
  127. void dfs(int u) {
  128. for(int i = head[u] ; i ; i = E[i].next) {
  129. int v = E[i].to;
  130. dis[v] = dis[u] + E[i].len;
  131. fa[v][0] = u;
  132. dfs(v);
  133. }
  134. }
  135. void pre_Process() {
  136. dfs(1);
  137. for(int j = 1 ; j <= 18 ; ++j) {
  138. for(int i = 1 ; i <= Ncnt ; ++i) {
  139. fa[i][j] = fa[fa[i][j - 1]][j - 1];
  140. }
  141. }
  142. for(int i = 1 ; i <= Ncnt ; ++i) {
  143. if(ed[i]) {
  144. for(auto t : R[ed[i]]) {
  145. int h = ed[i] - t.fi + 1;
  146. int u = i;
  147. for(int l = 18 ; l >= 0 ; --l) {
  148. if(dis[fa[u][l]] >= h) u = fa[u][l];
  149. }
  150. qry[u].pb(mp(h,t.se));
  151. }
  152. }
  153. }
  154. }
  155. void Calc(int u) {
  156. for(int i = head[u] ; i ; i = E[i].next) {
  157. int v = E[i].to;
  158. Calc(v);
  159. rt[u] = Merge(rt[u],rt[v],1,N);
  160. }
  161. if(ed[u]) Insert(rt[u],ed[u],1,N);
  162. for(auto k : qry[u]) {
  163. int tmp,t;
  164. pii rg;
  165. int64 all = 0;
  166. //on the left
  167. if(tr[rt[u]].minv >= 1) {
  168. t = tr[rt[u]].minv;
  169. ans[k.se] += 1LL * (N - t) * (N - t - 1) / 2;
  170. }
  171. //on the right
  172. if(tr[rt[u]].maxv <= N) {
  173. t = tr[rt[u]].maxv - k.fi + 1;
  174. ans[k.se] += 1LL * (t - 1) * (t - 2) / 2;
  175. }
  176. //on the middle
  177. ans[k.se] += tr[rt[u]].sum[0];
  178. if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
  179. ans[k.se] -= 1LL * (tr[rt[u]].maxv - tr[rt[u]].minv) * k.fi;
  180. ans[k.se] += 1LL * (N - tr[rt[u]].maxv) * (tr[rt[u]].maxv - k.fi);
  181. }
  182. //on the left && middle
  183. all = 0;
  184. if(tr[rt[u]].minv <= N) {
  185. rg = mp(N + 1,0);
  186. int a = tr[rt[u]].minv;
  187. if(a + k.fi <= N) {
  188. all -= Query(rt[u],a + k.fi,N,1,N,1,tmp = 0,rg);
  189. if(rg.fi <= rg.se) {
  190. all += 1LL * N * (rg.se - rg.fi);
  191. all += 1LL * (N - rg.fi) * (rg.fi - k.fi - a + 1);
  192. }
  193. }
  194. }
  195. ans[k.se] -= all;
  196. //on the left && right
  197. if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
  198. int a = tr[rt[u]].minv,b = tr[rt[u]].maxv - k.fi + 1;
  199. if(a <= b) ans[k.se] -= 1LL * (b - a) * (b - a - 1) / 2;
  200. }
  201. //on the middle && right
  202. all = 0;
  203. if(tr[rt[u]].maxv >= 1) {
  204. rg = mp(N + 1,0);
  205. int a = tr[rt[u]].maxv - k.fi + 1;
  206. if(a - 1 >= 1) {
  207. all += Query(rt[u],1,a - 1,1,N,0,tmp = N + 1,rg);
  208. if(rg.fi <= rg.se) {
  209. all -= 1LL * k.fi * (rg.se - rg.fi);
  210. all += 1LL * (a - rg.se) * (rg.se - k.fi);
  211. }
  212. }
  213. }
  214. ans[k.se] -= all;
  215. //on the left && middle && right
  216. all = 0;
  217. if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
  218. int a = tr[rt[u]].minv,b = tr[rt[u]].maxv - k.fi + 1;
  219. rg = mp(N + 1,0);
  220. if(a + k.fi <= b - 1) {
  221. all += Query(rt[u],a + k.fi,b - 1,1,N,0,tmp = N + 1,rg);
  222. if(rg.fi <= rg.se) {
  223. all -= 1LL * (rg.se - rg.fi) * (k.fi + a - 1);
  224. all += 1LL * (b - rg.se) * (rg.se - k.fi + 1 - a);
  225. }
  226. }
  227. }
  228. ans[k.se] += all;
  229. }
  230. }
  231. void Solve() {
  232. Calc(1);
  233. for(int i = 1 ; i <= Q ; ++i) {
  234. out(ans[i]);enter;
  235. }
  236. }
  237. }
  238. using SuffixTree::ed;
  239. using SuffixTree::add;
  240. namespace SAM {
  241. struct node {
  242. node *nxt[11],*per;
  243. int len,cnt;
  244. }pool[MAXN],*tail = pool,*root,*last,*que[MAXN];
  245. int c[MAXN];
  246. void Init() {
  247. root = last = tail++;
  248. root->len = 0;root->cnt = 0;root->per = NULL;
  249. }
  250. void build_SAM(int c,int len) {
  251. node *nowp = tail++,*p;
  252. nowp->len = len;nowp->cnt = 1;
  253. for(p = last ; p && !p->nxt[c] ; p = p->per) {
  254. p->nxt[c] = nowp;
  255. }
  256. if(!p) nowp->per = root;
  257. else {
  258. node *q = p->nxt[c];
  259. if(q->len == p->len + 1) nowp->per = q;
  260. else {
  261. node *copyq = tail++;
  262. *copyq = *q;
  263. copyq->cnt = 0;
  264. copyq->len = p->len + 1;
  265. q->per = nowp->per = copyq;
  266. for( ; p && p->nxt[c] == q ; p = p->per) {
  267. p->nxt[c] = copyq;
  268. }
  269. }
  270. }
  271. last = nowp;
  272. }
  273. void build_ST() {
  274. int m = tail - pool;
  275. for(int i = 0 ; i < m ; ++i) {
  276. c[pool[i].len]++;
  277. }
  278. for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
  279. for(int i = 0 ; i < m ; ++i) {
  280. que[c[pool[i].len]--] = &pool[i];
  281. }
  282. for(int i = m ; i >= 1 ; --i) {
  283. if(que[i]->per) {
  284. int u = que[i] - pool + 1,f = que[i]->per - pool + 1;
  285. add(f,u,que[i]->len - que[i]->per->len);
  286. if(que[i]->cnt) ed[u] = que[i]->len;
  287. }
  288. }
  289. SuffixTree::Ncnt = m;
  290. }
  291. }
  292. void Solve() {
  293. read(N);read(Q);
  294. scanf("%s",s + 1);
  295. SAM::Init();
  296. for(int i = 1 ; i <= N ; ++i) {
  297. SAM::build_SAM(s[i] - '0',i);
  298. }
  299. SAM::build_ST();
  300. int l,r;
  301. for(int i = 1 ; i <= Q ; ++i) {
  302. read(l);read(r);
  303. R[r].pb(mp(l,i));
  304. }
  305. SegmentTree::Init();
  306. SuffixTree::pre_Process();
  307. SuffixTree::Solve();
  308. }
  309. int main() {
  310. #ifdef ivorysi
  311. freopen("f1.in","r",stdin);
  312. #endif
  313. Solve();
  314. }

【LOJ】#2479. 「九省联考 2018」制胡窜的更多相关文章

  1. Loj #2479. 「九省联考 2018」制胡窜

    Loj #2479. 「九省联考 2018」制胡窜 题目描述 对于一个字符串 \(S\),我们定义 \(|S|\) 表示 \(S\) 的长度. 接着,我们定义 \(S_i\) 表示 \(S\) 中第 ...

  2. LOJ #2473. 「九省联考 2018」秘密袭击

    #2473. 「九省联考 2018」秘密袭击 链接 分析: 首先枚举一个权值W,计算这个多少个连通块中,第k大的数是这个权值. $f[i][j]$表示到第i个节点,有j个大于W数的连通块的个数.然后背 ...

  3. LOJ#2471「九省联考 2018」一双木棋 MinMax博弈+记搜

    题面 戳这里 题解 因为每行取的数的个数是单调不增的,感觉状态数不会很多? 怒而记搜,结果过了... #include<bits/stdc++.h> #define For(i,x,y) ...

  4. @loj - 2478@「九省联考 2018」林克卡特树

    目录 @description@ @solution@ @part - 1@ @part - 2@ @accepted code@ @details@ @description@ 小 L 最近沉迷于塞 ...

  5. 「九省联考 2018」IIIDX 解题报告

    「九省联考 2018」IIIDX 这什么鬼题,送的55分要拿稳,实测有60? 考虑把数值从大到小摆好,每个位置\(i\)维护一个\(f_i\),表示\(i\)左边比它大的(包括自己)还有几个数可以选 ...

  6. LOJ 2743(洛谷 4365) 「九省联考 2018」秘密袭击——整体DP+插值思想

    题目:https://loj.ac/problem/2473 https://www.luogu.org/problemnew/show/P4365 参考:https://blog.csdn.net/ ...

  7. [loj 2478][luogu P4843]「九省联考 2018」林克卡特树

    传送门 Description 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一 ...

  8. loj2472 「九省联考 2018」IIIDX

    ref #include <algorithm> #include <iostream> #include <cstdio> using namespace std ...

  9. [BZOJ 5252][LOJ 2478][九省联考2018] 林克卡特树

    [BZOJ 5252][LOJ 2478][九省联考2018] 林克卡特树 题意 给定一个 \(n\) 个点边带权的无根树, 要求切断其中恰好 \(k\) 条边再连 \(k\) 条边权为 \(0\) ...

随机推荐

  1. hdwiki5.1 词条参考资料都是相同的问题解决

    HDWiki 5.1版本存在的BUG,当一个词条添加参考资料后,所有词条均显示以此相同的参考资料:而且参考资料不能修改和删除! 解决方法如下: 找到后台文件:\model\reference.clas ...

  2. 在Mac上配置全局的Git忽略文件

    现在同时搞着好几个项目,在Xcode.IDEA.Eclipse之间频繁的切换,每个项目的忽略文件列表都不一样,每个项目都有一个.gitignore,甚是麻烦,今天网上拔出来一个设置全局忽略的办法,记录 ...

  3. 洛谷 P2303 [SDOi2012]Longge的问题 解题报告

    P2303 [SDOi2012]Longge的问题 题目背景 SDOi2012 题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数\(N\),你需要 ...

  4. Nexus Repository Manager 3(CVE-2019-7238) 远程代码执行漏洞分析和复现

    0x00 漏洞背景 Nexus Repository Manager 3是一款软件仓库,可以用来存储和分发Maven,NuGET等软件源仓库.其3.14.0及之前版本中,存在一处基于OrientDB自 ...

  5. C++时间标准库时间time

    转自:http://www.cnblogs.com/yukaizhao/archive/2011/04/29/cpp_time_system_time.html (玉开) C++标准库中的时间需要引用 ...

  6. 20181105 Timer(慕课网)

    定时任务调度 基于给定的时间点,给定的时间间隔或者给定的执行次数自动执行的任务 Java中的定时调度工具 Timer JDK提供,不许引入 功能简单,能用Timer尽量用 Quartz 需要引入 功能 ...

  7. nova-api源码分析(APP的创建)

    目录结构如下: 上面介绍了nova-api发布所用到的一些lib库,有了上面的基础知识,再来分析nova-api的发布流程,就比较轻松了.nova-api可以提供多种api服务:ec2, osapi_ ...

  8. 一些常见修改命令(针对ubuntu 14.04 持续更新中...)

    1.PS1 在哪: echo $PS1    vi /etc/bash.bashrc      /W输出最后一个目录    /w输出完整目录 2.设置静态IP地址:vim /etc/network/i ...

  9. springboot(五):springboot整合shiro-登录认证和权限管理

    http://z77z.oschina.io/ http://www.cnblogs.com/aqsunkai/category/982003.html https://www.cnblogs.com ...

  10. CSS规范 - 典型错误--(来自网易)

    不符合NEC规范的选择器用法 .class{} 不要以一个没有类别的样式作为主选择器,这样的选择器只能作为后代选择器使用,比如.m-xxx .class{}.        .m-xxx div{} ...