「JOISC 2020 Day3」收获

「JOISC 2020 Day3」收获

分类讨论.jpg

分析一棵苹果树被不断摘掉的过程,找到第一个摘它的人 \(i\)

此后,每次摘它的人,就是 \(i\) 前面第一个距离它 \(\ge C\) 的人,不妨设其为 \(nxt_i\)

显然, \(i,nxt_i\) 的关系,会构成基环内向树森林,每条内向边有一个权值 \(w_i\)

容易 \(O(n)\) 尺取得到 \(nxt_i,w_i\) ,考虑选择环上的一个点 \(u\) ,断开 \(u\) 对应的内向边,得到一棵树

Snipaste_2021-03-13_13-26-19.png

处理得到环长 \(len_u\) ,令 \(dis_u=w_u\) ,树上每个点的 \(dis_v=dis_{nxt_v}+w_v\)

考虑一棵苹果树被第一次摘的情况,用一个二元组表示 \((v,t)\) ,即被 \(v\)\(t\) 时刻摘掉

我们认为是苹果树在基环内向树上走

1.苹果树不跨过 \(u\) 时的贡献

此时相当于每个 \((v,t)\) 在往根节点走,贡献来自每个查询 \((x,d)\) 的子树

即满足 \(v\in subtree_x,dis_v-dis_x+t\leq d\)

离散之后可以用简单的 询问离线+ \(dfs\) 作差+树状数组解决

\[ \ \]

2.跨过 \(u\) ,先将苹果树的贡献移动到 \(last\) 上,变为 \((last,t'=t+dis_v)\)

对于每个询问,显然必须满足 \(x\) 在环上

我们也可以令 \(d'=d-(len_u-dis_v)\) ,同样将 \(x\) 移动到 \(last\)

此时只需要考虑每个 \(t'\) 对于 \(d'\) 的贡献

按照 \(len_u\) ,我们可以将 \(t',d'\) 分段,每段都是 \([i\cdot len_u,(i+1)\cdot len_u)\) 的形式

2-1.对于不是同一段内的,每个 \(t'\) 的对于 \(d'\) 的贡献次数 就是 段编号 之差

2-2.同一段内,就是满足 \(t'\leq d'\)\(t'\mod len_u\leq d'\mod len_u\) 的个数

将所有 \(d',t'\) 排序后依次处理,容易通过参数分离处理2-1

对于2-2,将 \(t'\mod len_u\) 离散后可以用树状数组处理

Loj Submission

空间复杂度为 \(O(n)\) ,时间复杂度为 \(O(n\log n)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include<bits/stdc++.h>
using namespace std;
using ll=int64_t;
#define pb push_back
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
ll rd(){
char c;ll s=0;
while(c=getchar(),c<48);
do s=(s<<1)+(s<<3)+(c^'0');
while(c=getchar(),c>47);
return s;
}
enum{N=200010};
int n,m,q,nxt[N],L,C,C2,A[N*2],B[N],id[N],incir[N];
vector <int> E[N],G[N];
ll H[N],dis[N],H2[N],len[N],ans[N];
void dfs(int u){
for(int i:E[u]) H[++C]=dis[u]+i;
vector <int> tmp;
for(int v:G[u]) {
if(id[v]==id[u]) continue;
tmp.pb(v),id[v]=id[u];
dis[v]+=dis[u],dfs(v);
}
G[u]=tmp;
}

struct Node{ ll d; int id; };
vector <Node> Q[N],que;
vector <ll> upd;
struct BIT{
int s[N],n;
void Init(int m){ n=m,memset(s,0,(n+1)<<2); }
void Add(int p,int x) { while(p<=n) s[p]+=x,p+=p&-p; }
int Que(int p) {
int res=0;
while(p) res+=s[p],p-=p&-p;
return res;
}
} T,X;

// dfs作差处理情况1
void dfs2(int u){
for(Node &i:Q[u]) {
// 如果满足查询点在环上,就要加入2-1,2-2的计算
if(incir[u]) {
ll d=i.d-(len[id[u]]-dis[u]);
if(d>=0) que.pb((Node){d,i.id});
}
// dfs作差-1
ans[i.id]-=T.Que(i.d=upper_bound(H+1,H+C+1,dis[u]+i.d)-H-1);
}
for(int i:E[u]) T.Add(lower_bound(H+1,H+C+1,dis[u]+i)-H,1),upd.pb(dis[u]+i);
for(int v:G[u]) dfs2(v);
// dfs作差+1
for(Node i:Q[u]) ans[i.id]+=T.Que(i.d);
}

int main(){
n=rd(),m=rd(),L=rd(),C=rd();
rep(i,0,n-1) A[i]=rd(),A[i+n]=A[i]+L;
rep(i,0,m-1) B[i]=rd();
int C_=C%L,p=0;
// 尺取预处理i,nxt[i],w[i]
rep(i,n,n*2-1) {
while(p<i && A[i]-A[p+1]>=C_) p++;
nxt[i-n]=p%n,G[p%n].pb(i-n);
dis[i-n]=C-C_+A[i]-A[p];
}
p=0;
// 预处理(v,t)
rep(i,0,m-1) {
while(p<n*2-1 && B[i]+L>=A[p+1]) p++;
E[p%n].pb(B[i]+L-A[p]);
}
C=0;
// 断环构建树
rep(i,0,n-1) id[i]=-2;
rep(i,0,n-1) if(id[i]==-2) {
int u=i;
for(;~id[u];u=nxt[u]) id[u]=-1;
id[u]=u,len[u]=dis[u],incir[u]=1;
for(int v=nxt[u];v!=u;v=nxt[v]) len[u]+=dis[v],incir[v]=1;
dfs(u);
}
sort(H+1,H+C+1),T.Init(C=unique(H+1,H+C+1)-H-1);
// 离线询问,权值离散
rep(i,1,q=rd()) {
int u=rd()-1; ll d=rd();
Q[u].pb((Node){d,i});
}
rep(i,0,n-1) if(id[i]==i) {
que.clear(),upd.clear();
dfs2(i);
sort(upd.begin(),upd.end()),sort(que.begin(),que.end(),[&](Node x,Node y){ return x.d<y.d; });
C2=0;
for(ll x:upd) H2[++C2]=x%len[i];
sort(H2+1,H2+C2+1),X.Init(C2=unique(H2+1,H2+C2+1)-H2-1);
auto it=upd.begin();
ll s=0,c=0;
for(Node &q:que) {
while(it!=upd.end() && *it<=q.d)
X.Add(lower_bound(H2+1,H2+C2+1,*it%len[i])-H2,1),s+=*(it++)/len[i],c++;
// 参数分离处理2-1
ans[q.id]+=q.d/len[i]*c-s;
// 树状数组查询2-2
ans[q.id]+=X.Que(upper_bound(H2+1,H2+C2+1,q.d%len[i])-H2-1);
}
}
rep(i,1,q) printf("%lld\n",ans[i]);
}