Warning: session_start(): open(/tmp/sess_9836968e8699b9b7793a6287c9c81edc, O_RDWR) failed: No space left on device (28) in /data/wiki/inc/init.php on line 239

Warning: session_start(): Failed to read session data: files (path: ) in /data/wiki/inc/init.php on line 239

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/auth.php on line 430
Writing /data/wiki/data/cache/d/de2edb2fcb553ea79b79c722a4e13dbc.captchaip failed

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/actions.php on line 38

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/lib/tpl/dokuwiki/main.php on line 12
2020-2021:teams:legal_string:组队训练比赛记录:contest4 [CVBB ACM Team]

用户工具

站点工具


2020-2021:teams:legal_string:组队训练比赛记录:contest4

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:legal_string:组队训练比赛记录:contest4 [2021/07/20 17:29]
jxm2001
2020-2021:teams:legal_string:组队训练比赛记录:contest4 [2021/07/25 00:00] (当前版本)
jxm2001 [题解]
行 2: 行 2:
  
 ====== 题解 ====== ====== 题解 ======
 +
 +===== A. Arithmetic Progression =====
 +
 +==== 题意 ====
 +
 +给定一个序列,问有多少连续子串从小到大排列后可以构成等差序列。
 +
 +==== 题解 ====
 +
 +给定一个序列 $a_1,​a_2\cdots a_n$,则 $\max(a_i)-\min(a_i)\ge (n-1)\times \text{gcd}(a_2-a_1,​a_3-a_2\cdots a_n-a_{n-1})$。
 +
 +等号成立充要条件为 $a_1,​a_2\cdots a_n$ 从小到大排列后可以构成等差序列。具体见 [[..:​jxm2001:​other:​结论_3#​等差序列判定|证明]]。
 +
 +于是问题转化为统计有多少区间满足 $\max(a[l\sim r])-\min(a[l\sim r])=(r-l)\text{gcd}(a_{l+1}-a_l\cdots a_r-a_{r-1})$。
 +
 +考虑构造序列 $b_i=\max(a[i\sim r])-\min(a[i\sim r])+i\times \text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$。
 +
 +线段树维护区间最小值和最小值个数,对固定的 $r$,统计 $[i,r](i\lt r)$ 贡献时,枚举 $\text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$ 不同的区间。
 +
 +对每个区间将 $b_i=r\times \text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$ 的数值个数计入贡献。
 +
 +易知 $\text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$ 不同的区间最多只有 $O(\log v)$ 个,于是总询问次数 $O(n\log v)$。
 +
 +然后移动 $r$ 更新区间,需要维护 $\max(a[l\sim r]),​\min(a[l\sim r]),i\times \text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$ 的变化。
 +
 +对 $\max(a[l\sim r]),​\min(a[l\sim r])$ 的变化可以用单调栈维护,总更新次数 $O(n)$。
 +
 +对 $i\times \text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$ 的变化,可以维护 $\text{gcd}(a_{i+1}-a_i\cdots a_r-a_{r-1})$ 不同的段。
 +
 +每次 $r$ 变化时暴力遍历 $O(\log v)$ 个段,每个段的值发生变化时暴力遍历整个段更新。
 +
 +由于每个位置的 $\text{gcd}$ 值最多变化 $O(\log v)$ 次,所以总更新次数 $O(n\log v)$。
 +
 +于是线段树只需要支持区间加操作和区间查询操作,总时间复杂度 $O(n\log n\log v)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=1e5+5;
 +struct Node{
 + LL v;
 + int cnt;
 + Node operator + (const Node &​b)const{
 + Node c;
 + if(v==b.v)c.v=v,​c.cnt=cnt+b.cnt;​
 + else if(v<​b.v)c=*this;​
 + else c=b;
 + return c;
 + }
 +}s[MAXN<<​2];​
 +LL tag[MAXN<<​2];​
 +int lef[MAXN<<​2],​rig[MAXN<<​2];​
 +void push_up(int k){
 + s[k]=s[k<<​1]+s[k<<​1|1];​
 +}
 +void push_add(int k,LL v){
 + s[k].v+=v;
 + tag[k]+=v;
 +}
 +void push_down(int k){
 + if(tag[k]){
 + push_add(k<<​1,​tag[k]);​
 + push_add(k<<​1|1,​tag[k]);​
 + tag[k]=0;
 + }
 +}
 +void build(int k,int L,int R){
 + lef[k]=L,​rig[k]=R,​tag[k]=0;​
 + if(L==R){
 + s[k].v=0;
 + s[k].cnt=1;​
 + return;
 + }
 + int M=L+R>>​1;​
 + build(k<<​1,​L,​M);​
 + build(k<<​1|1,​M+1,​R);​
 + push_up(k);​
 +}
 +void update(int k,int L,int R,LL v){
 + if(L<​=lef[k]&&​rig[k]<​=R){
 + push_add(k,​v);​
 + return;
 + }
 + push_down(k);​
 + int mid=lef[k]+rig[k]>>​1;​
 + if(mid>​=L)
 + update(k<<​1,​L,​R,​v);​
 + if(mid<​R)
 + update(k<<​1|1,​L,​R,​v);​
 + push_up(k);​
 +}
 +Node query(int k,int L,int R){
 + if(L<​=lef[k]&&​rig[k]<​=R)
 + return s[k];
 + push_down(k);​
 + int mid=lef[k]+rig[k]>>​1;​
 + if(mid>​=R)
 + return query(k<<​1,​L,​R);​
 + else if(mid<​L)
 + return query(k<<​1|1,​L,​R);​
 + else
 + return query(k<<​1,​L,​R)+query(k<<​1|1,​L,​R);​
 +}
 +int gcd(int a,int b){
 + while(b){
 + int t=b;
 + b=a%b;
 + a=t;
 + }
 + return a;
 +}
 +int a[MAXN],​st1[MAXN],​st2[MAXN];​
 +pair<​int,​int>​ st3[MAXN];
 +int main()
 +{
 + int T=read_int();​
 + while(T--){
 + int n=read_int();​
 + _rep(i,​1,​n)a[i]=read_int();​
 + build(1,​1,​n);​
 + LL ans=n;
 + int top1=0,​top2=0,​top3=0;​
 + _rep(i,​1,​n){
 + while(top1&&​a[st1[top1]]<​=a[i]){
 + update(1,​st1[top1-1]+1,​st1[top1],​a[i]-a[st1[top1]]);​
 + top1--;
 + }
 + st1[++top1]=i;​
 + while(top2&&​a[st2[top2]]>​=a[i]){
 + update(1,​st2[top2-1]+1,​st2[top2],​a[st2[top2]]-a[i]);​
 + top2--;
 + }
 + st2[++top2]=i;​
 + if(i==1)continue;​
 + st3[++top3]=make_pair(abs(a[i]-a[i-1]),​i-1);​
 + update(1,​i-1,​i-1,​1LL*(i-1)*st3[top3].first);​
 + for(int j=top3-1;​j;​j--){
 + if(st3[j+1].first%st3[j].first!=0){
 + int temp=st3[j].first;​
 + st3[j].first=gcd(st3[j].first,​st3[j+1].first);​
 + _rep(k,​st3[j-1].second+1,​st3[j].second)
 + update(1,​k,​k,​1LL*k*(st3[j].first-temp));​
 + }
 + }
 + int temp=0;
 + _rep(j,​1,​top3){
 + if(st3[j].first==st3[temp].first)
 + st3[temp].second=st3[j].second;​
 + else
 + st3[++temp]=st3[j];​
 + }
 + top3=temp;​
 + _rep(j,​1,​top3){
 + Node temp=query(1,​st3[j-1].second+1,​st3[j].second);​
 + if(temp.v==1LL*i*st3[j].first)
 + ans+=temp.cnt;​
 + }
 + }
 + enter(ans);​
 + }
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
  
 ===== B. Cannon ===== ===== B. Cannon =====
行 15: 行 178:
 对于第一个子问题,我们发现可以把 $k!$ 提出来,之后对于里面的式子,可以变成两个组合数的乘积,再利用组合数的常用结论,可以推出其等于 $2^{k}k!C(n+m,​k)$ ,这个式子可以通过预处理 $2$ 的方幂,阶乘以及阶乘的逆来 $O(1)$ 求得,于是总共复杂度 $O(n+m)$ 。 对于第一个子问题,我们发现可以把 $k!$ 提出来,之后对于里面的式子,可以变成两个组合数的乘积,再利用组合数的常用结论,可以推出其等于 $2^{k}k!C(n+m,​k)$ ,这个式子可以通过预处理 $2$ 的方幂,阶乘以及阶乘的逆来 $O(1)$ 求得,于是总共复杂度 $O(n+m)$ 。
  
-对于第二个子问题, $2^{k} \sum {\frac {n!} {(n-i)!}} {\frac {m!} {(m-(k-i))!}} = 2^{k} \sum {\frac {n!m!} {(n+m-k)!}} {\frac {(n+m-k)!} {(n-i)!(m-(k-i))!}} = 2^{k} {\frac {n!m!} {(n+m-k)!}} \sum_{i=n-k}^{n} C(n+m-k,​i)$ ​+对于第二个子问题, $2^{k} \sum {\frac {n!} {(n-i)!}} {\frac {m!} {(m-(k-i))!}} = 2^{k} \sum {\frac {n!m!} {(n+m-k)!}} {\frac {(n+m-k)!} {(n-i)!(m-(k-i))!}} = 2^{k} {\frac {n!m!} {(n+m-k)!}} \sum_{i=k-m}^{n} C(n+m-k,​i)$ ​
  
 虽然没有直接的结论,但是这个可以用步移算法来解决。 虽然没有直接的结论,但是这个可以用步移算法来解决。
2020-2021/teams/legal_string/组队训练比赛记录/contest4.1626773358.txt.gz · 最后更改: 2021/07/20 17:29 由 jxm2001