题目列表:
树
跑步训练
金币
棋盘染色
矩形
o
Inside information
树
求大小为 n n n 的所有带标号无根树的直径的和模 P P P 的值。
1
⩽
n
⩽
500
,
1
0
8
⩽
P
⩽
2147483647
1 \leqslant n \leqslant 500,10^8 \leqslant P \leqslant 2147483647
1⩽n⩽500,108⩽P⩽2147483647
时间限制4s,空间限制1GB
首先枚举树的直径上节点数
d
d
d。如果
d
d
d 为偶数,则树可以看做两棵深度为
d
2
\frac{d}2
2d 的有根树合并得到的。设
f
i
,
j
f_{i,j}
fi,j 为节点数为
i
i
i,深度不超过
j
j
j 的带标号有根树数量,
g
i
,
j
g_{i,j}
gi,j 为总节点数为
i
i
i,最大深度不超过
j
j
j 的带标号森林数量。则当
d
d
d 为偶数时答案为:
∑
i
=
1
n
−
1
(
f
i
,
d
2
−
f
i
,
d
2
−
1
)
(
f
n
−
i
,
d
2
−
f
n
−
i
,
d
2
−
1
)
(
n
−
1
i
)
\sum_{i=1}^{n-1}(f_{i,\frac{d}2}-f_{i,\frac{d}2-1})(f_{n-i,\frac{d}2}-f_{n-i,\frac{d}2-1})\binom{n-1}{i}
∑i=1n−1(fi,2d−fi,2d−1)(fn−i,2d−fn−i,2d−1)(in−1)。
当
d
d
d 为奇数时,则原树可以看做由一个点为根,并有至少两个儿子的子树深度为
d
−
1
2
\frac{d-1}2
2d−1 的树。统计答案时用深度为
d
−
1
2
\frac{d-1}2
2d−1 的森林方案数减去一棵深度为
d
−
1
2
\frac{d-1}2
2d−1,剩下的森林深度小于
d
−
1
2
\frac{d-1}2
2d−1 的树方案。则当
d
d
d 为奇数时的答案为:
n
(
g
n
−
1
,
d
−
1
2
−
g
n
−
1
,
d
−
1
2
−
1
−
∑
i
=
1
n
−
1
(
f
i
,
d
−
f
i
,
d
−
1
)
g
n
−
1
−
i
,
d
−
1
(
n
−
1
j
)
)
n(g_{n-1,\frac{d-1}2}-g_{n-1,\frac{d-1}2-1}-\sum_{i=1}^{n-1}(f_{i,d}-f_{i,d-1})g_{n-1-i,d-1} \binom{n-1}{j})
n(gn−1,2d−1−gn−1,2d−1−1−∑i=1n−1(fi,d−fi,d−1)gn−1−i,d−1(jn−1))
最后的问题就是求
f
,
g
f,g
f,g。根据定义,有DP式子:
f
n
,
i
=
n
g
n
−
1
,
i
−
1
f_{n,i}=n g_{n-1,i-1}
fn,i=ngn−1,i−1
对于森林来说,算树按顺序加入的方案数:
g
n
,
i
=
∑
j
=
1
n
f
j
,
i
g
n
−
j
,
i
(
n
−
1
n
−
i
)
g_{n,i}=\sum_{j=1}^n f_{j,i} g_{n-j,i} \binom{n-1}{n-i}
gn,i=∑j=1nfj,ign−j,i(n−in−1)
时间复杂度
O
(
n
3
)
O(n^3)
O(n3),空间复杂度
O
(
n
2
)
O(n^2)
O(n2)
#include<iostream>
using namespace std;
#define R register int
#define U unsigned int
#define L long long
inline U Add(U x,const U y,U P){
x+=y;
return x<P?x:x-P;
}
U C[501][501],f[501][501],g[501][501];
int main(){
int n,d;
cin>>n;
if(n==1){
cout<<0;
return 0;
}
U P,ans=0,cur;
cin>>P;
for(R i=0;i<=n;i++){
C[i][0]=1;
for(R j=1;j<=i;j++){
C[i][j]=Add(C[i-1][j-1],C[i-1][j],P);
}
}
g[0][0]=1;
for(R i=1;i<=n;i++){
for(R j=1;j<=n;j++){
f[i][j]=(L)j*g[i-1][j-1]%P;
}
g[i][0]=1;
for(R j=0;j<=n;j++){
for(R k=1;k<=n-j;k++){
g[i][j+k]=((L)g[i][j]*f[i][k]%P*C[j+k-1][j]+g[i][j+k])%P;
}
}
}
for(R i=1;i<=n;i++){
d=i>>1;
if((i&1)==1){
cur=g[d][n-1];
if(d!=0){
cur=Add(cur,P-g[d-1][n-1],P);
}
for(R j=d;j!=n;j++){
cur=Add(cur,P-(L)(f[d][j]+(d==0?0:P-f[d-1][j]))*g[d-1][n-1-j]%P*C[n-1][j]%P,P);
}
ans=((i-1ll)*cur*n+ans)%P;
}else{
cur=0;
for(R j=d;j<=n-d;j++){
cur=((L)(f[d][j]+P-f[d-1][j])*(f[d][n-j]+P-f[d-1][n-j])%P*C[n-1][j]+cur)%P;
}
ans=((i-1ll)*cur+ans)%P;
}
}
cout<<ans;
return 0;
}
跑步训练
给定一个 n n n 个点的树的平面图,共 m m m 个人各自从某些点向某个方向出发,每天一直沿着右手方向跑 p p p 条边,把最后的一条边封掉,第二天接着原来的方向继续跑步。求每个人最后在第几天被封在哪个节点,求每条边在第几天被哪个人封堵。
1
⩽
n
⩽
250000
,
0
⩽
m
⩽
50000
,
1
⩽
p
⩽
1
0
9
1 \leqslant n \leqslant 250000,0 \leqslant m \leqslant 50000,1 \leqslant p \leqslant 10^9
1⩽n⩽250000,0⩽m⩽50000,1⩽p⩽109
时间限制3s,空间限制512MB
在一棵树中一天的问题可以建立边的括号序来解决。用线段树维护括号序,一个人最后走的一条边可以在线段树上二分出来。根据每个人一天最后的节点将当前的数分为多棵子树,像虚树一样求出子树之间的祖先后代关系,将人分给子树,递归子树,最后将子树的括号序在线段树中删掉。代码细节很多。
时间复杂度
O
(
(
n
+
m
)
log
n
)
O((n+m) \log n)
O((n+m)logn),空间复杂度
O
(
n
+
m
)
O(n+m)
O(n+m)
#include<stdio.h>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
#define R register int
#define I inline
#define N 250001
#define M 500002
struct Runner{
int RunnerId,CurEdge;
};
I auto Pair(int d,int c){
Runner res;
res.RunnerId=d;
res.CurEdge=c;
return res;
}
vector<int>G[N];
int par[N],ansR[N][2],inE[N],vis[M],outE[N],ansE[N][2],Start[M],in[N],out[N],f[N],pos[N][2],End[M],dep[N],ct,tot;
bool dir[M];
I bool CompareDFN(int x,int y){
return in[x]<in[y];
}
I int GetF(int x){
if(par[x]==x){
return x;
}
par[x]=GetF(par[x]);
return par[x];
}
I void DFS(int x){
tot++;
in[x]=tot;
dep[x]=dep[f[x]]+1;
inE[x]=ct+1;
for(int T:G[x]){
ct++;
pos[T][0]=ct;
Start[ct]=x;
End[ct]=T;
DFS(T);
ct++;
pos[T][1]=ct;
Start[ct]=T;
End[ct]=x;
dir[ct]=true;
}
out[x]=tot;
outE[x]=ct;
}
struct SegmentNode{
int Sum;
bool tag;
}t[2000000];
I void Init(int p,int lf,int rt){
t[p].Sum=rt-lf+1;
if(lf!=rt){
Init(p<<1,lf,lf+rt>>1);
Init(p<<1|1,lf+rt+2>>1,rt);
}
}
I void PutDown(int p,bool f){
if(t[p].tag==true){
t[p].Sum=0;
if(f==true){
t[p<<1].tag=t[p<<1|1].tag=true;
}
t[p].tag=false;
}
}
I void Clear(int p,int lf,int rt,const int l,const int r){
if(l<=lf&&rt<=r){
t[p].tag=true;
}else{
PutDown(p,true);
int mid=lf+rt>>1;
if(l<=mid){
Clear(p<<1,lf,mid,l,r);
}
if(r>mid){
Clear(p<<1|1,mid+1,rt,l,r);
}
PutDown(p<<1,lf!=mid);
PutDown(p<<1|1,mid+1!=rt);
t[p].Sum=t[p<<1].Sum+t[p<<1|1].Sum;
}
}
I int GetSum(int p,int lf,int rt,const int l,const int r){
PutDown(p,lf!=rt);
if(l<=lf&&rt<=r){
return t[p].Sum;
}
int res=0,mid=lf+rt>>1;
if(l<=mid){
res=GetSum(p<<1,lf,mid,l,r);
}
if(r>mid){
res+=GetSum(p<<1|1,mid+1,rt,l,r);
}
return res;
}
I int Find(int p,int lf,int rt,const int l,const int r,int&k){
PutDown(p,lf!=rt);
if(l<=lf&&rt<=r&&t[p].Sum<k){
k-=t[p].Sum;
return-1;
}
if(lf==rt){
return lf;
}
int mid=lf+rt>>1,res=-1;
if(l<=mid){
res=Find(p<<1,lf,mid,l,r,k);
}
if(res!=-1){
return res;
}
return Find(p<<1|1,mid+1,rt,l,r,k);
}
I void Solve(int root,vector<Runner>&C,int Time,const int p){
int El=inE[root],Er=outE[root],sum;
if(C.empty()==true){
return Clear(1,1,ct,El-1,Er+1);
}
sum=GetSum(1,1,ct,El,Er);
if(sum==0){
for(auto T:C){
ansR[T.RunnerId][0]=Time;
ansR[T.RunnerId][1]=root;
}
return Clear(1,1,ct,El-1,Er+1);
}
vector<int>A;
A.push_back(root);
Time++;
for(auto&T:C){
int c=T.CurEdge,rk;
if(c<El||c>Er){
rk=0;
}else{
rk=GetSum(1,1,ct,El,c);
}
rk=(rk+p)%sum;
if(rk==0){
rk=sum;
}
int cur=Find(1,1,ct,El,Er,rk),b;
b=dir[cur]==true?Start[cur]:End[cur];
ansE[b][0]=Time;
if(vis[cur]!=0){
par[GetF(T.RunnerId)]=vis[cur];
T.CurEdge=0;
}else{
if(ansE[b][1]==0){
ansE[b][1]=T.RunnerId;
}
A.push_back(b);
T.CurEdge=cur;
vis[cur]=T.RunnerId;
}
}
sort(A.begin(),A.end(),CompareDFN);
int s=unique(A.begin(),A.end())-A.begin();
map<int,int>Q;
for(R i=0;i!=s;i++){
Q[A[i]]=i;
}
vector<int>B(s),F(s);
int Top=0;
B[0]=0;
for(R i=1;i!=s;i++){
while(Top!=0){
int x=B[Top];
if(in[A[x]]<in[A[i]]&&in[A[i]]<=out[A[x]]){
break;
}
F[B[Top]]=B[Top-1];
Top--;
}
Top++;
B[Top]=i;
}
while(Top!=0){
F[B[Top]]=B[Top-1];
Top--;
}
vector<vector<Runner>>D(s);
for(auto T:C){
int x=T.CurEdge;
if(x!=0){
if(dir[x]==true){
D[F[Q[Start[x]]]].push_back(T);
}else{
D[Q[End[x]]].push_back(T);
}
}
}
for(R i=s-1;i!=-1;i--){
Solve(A[i],D[i],Time,p);
}
if(root!=1){
Clear(1,1,ct,El-1,Er+1);
}
}
int main(){
int n,m,p,a,b;
scanf("%d%d%d",&n,&m,&p);
for(int i=2;i<=n;i++){
scanf("%d",f+i);
G[f[i]].push_back(i);
}
vector<Runner>C;
DFS(1);
for(R i=1;i<=m;i++){
scanf("%d%d",&a,&b);
int x;
if(a==f[b]){
x=pos[b][0]-1;
}else{
x=pos[a][1]-1;
}
if(x==0){
x=ct;
}
C.push_back(Pair(i,x));
par[i]=i;
}
Init(1,1,ct);
Solve(1,C,0,p);
for(R i=1;i<=m;i++){
int x=GetF(i);
printf("%d %d\n",ansR[x][0],ansR[x][1]);
}
for(R i=2;i<=n;i++){
printf("%d %d\n",ansE[i][0],ansE[i][1]);
}
return 0;
}
金币
现有若干袋金币,每袋有 k k k 个金币,其中一个袋子里全是假金币,其他袋子里都是真金币,假金币比真的轻。每次用天平称金币,要求天平两边的金币总数相同,称完可以得到两边的金币重量差。给定称量次数限制 n n n,求最多可以在多少袋子中确定装假金币的袋子。输出答案模 1000000007 1000000007 1000000007的值。
1
⩽
n
⩽
1
0
9
,
1
⩽
k
⩽
1
0
7
1 \leqslant n \leqslant 10^9,1 \leqslant k \leqslant 10^7
1⩽n⩽109,1⩽k⩽107
时间限制1s,空间限制512MB
每个袋子在每次称量中共有
2
k
+
1
2k+1
2k+1 中放在天平上的方法。若所有称量选取的金币数量最大公约数大于
1
1
1,那么就不能分辨出袋子之间的组合,因为看到的现象是本质相同的,所以答案就是本质不同的现象个数,即为:
∑
−
k
⩽
a
i
⩽
k
[
∣
gcd
(
a
)
∣
<
2
]
\sum_{-k \leqslant a_i \leqslant k} [|\gcd(a)|<2]
∑−k⩽ai⩽k[∣gcd(a)∣<2]
莫比乌斯反演并枚举非
0
0
0数的个数:
1
+
∑
i
=
1
n
(
n
i
)
∑
d
=
1
k
μ
(
d
)
[
k
d
]
i
2
i
1+\sum_{i=1}^n \binom{n}{i} \sum_{d=1}^k \mu(d) [\frac{k}{d}]^i 2^i
1+∑i=1n(in)∑d=1kμ(d)[dk]i2i
改变求和顺序:
1
+
∑
d
=
1
k
μ
(
d
)
∑
i
=
1
n
(
n
i
)
(
2
[
k
d
]
)
i
1+\sum_{d=1}^k \mu(d) \sum_{i=1}^n \binom{n}{i} (2[\frac{k}{d}])^i
1+∑d=1kμ(d)∑i=1n(in)(2[dk])i
根据二项式定理得到:
1
+
∑
d
=
1
k
μ
(
d
)
(
(
2
[
k
d
]
+
1
)
n
−
1
)
1+\sum_{d=1}^k \mu(d)((2[\frac{k}{d}]+1)^n-1)
1+∑d=1kμ(d)((2[dk]+1)n−1)
用整出分块即可。
时间复杂度
O
(
k
log
2
n
)
O(\sqrt k \log_2 n)
O(klog2n),空间复杂度
O
(
k
)
O(k)
O(k)
#include<stdio.h>
#define R register int
#define L long long
#define P 1000000007
inline int PowMod(int x,int y){
int res=1;
while(y!=0){
if((y&1)==1){
res=(L)res*x%P;
}
y>>=1;
x=(L)x*x%P;
}
return res;
}
int mu[10000001],prime[664579];
bool vis[10000001];
int main(){
mu[1]=1;
int n,k,t=0,r,ans=1;
scanf("%d%d",&n,&k);
for(R i=2;i<=k;i++){
if(vis[i]==false){
prime[t]=i;
mu[i]=-1;
t++;
}
for(R j=0;prime[j]*i<=k;j++){
vis[prime[j]*i]=true;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
mu[i]+=mu[i-1];
}
for(R i=1;i<=k;i=r+1){
t=k/i;
r=k/t;
ans=((PowMod(t<<1|1,n)-1ll+P)*(mu[r]-mu[i-1]+P)+ans)%P;
}
printf("%d",ans);
return 0;
}
棋盘染色
一个 n × m n \times m n×m 的棋盘,开始有些格子被染上了红色或者蓝色,有些格子没有颜色。将所有未染色的格子染上红色或者蓝色,使得棋盘中每个长宽都为偶数的矩形里面红色蓝色出现个数相同。求染色的方案数,答案对 1000000007 1000000007 1000000007取模。
1
⩽
n
,
m
⩽
1000
1 \leqslant n,m \leqslant 1000
1⩽n,m⩽1000
时间限制1s,空间限制128MB
观察规律可以发现矩阵填完颜色后要么所有行红蓝交替,要么所有列红蓝交替。所以答案就是行交替加列交替的方案数减行列都交替的方案数。
时空复杂度
O
(
n
m
)
O(nm)
O(nm)
#include<stdio.h>
#define R register int
#define P 1000000007
inline int Trans(const char c){
return c=='R';
}
char s[1000][1001];
int t[1500];
int main(){
int n,m,x,cur=1,ans=0;
scanf("%d%d",&n,&m);
for(R i=0;i!=n;i++){
scanf("%s",s[i]);
t[i]=2;
}
for(R i=0;i!=n;i++){
for(R j=0;j!=n;j++){
if(s[i][j]!='?'){
x=Trans(s[i][j])^j&1;
if(t[i]==2){
t[i]=x;
}else if(t[i]!=x){
goto BreakR;
}
}
}
}
for(R i=0;i!=n;i++){
if(t[i]==2){
cur<<=1;
if(cur>P){
cur-=P;
}
}
}
ans=cur;
BreakR:;
for(R i=0;i!=n;i++){
t[i]=2;
}
for(R i=0;i!=n;i++){
for(R j=0;j!=n;j++){
if(s[i][j]!='?'){
x=Trans(s[i][j])^i&1;
if(t[j]==2){
t[j]=x;
}else if(t[j]!=x){
goto BreakC;
}
}
}
}
cur=1;
for(R i=0;i!=n;i++){
if(t[i]==2){
cur<<=1;
if(cur>P){
cur-=P;
}
}
}
ans+=cur;
if(ans>=P){
ans-=P;
}
BreakC:;
cur=2;
for(R i=0;i!=n;i++){
for(R j=0;j!=n;j++){
if(s[i][j]!='?'){
x=Trans(s[i][j])^(i^j)&1;
if(cur==2){
cur=x;
}else if(cur!=x){
goto BreakT;
}
}
}
}
ans-=cur==0?1:cur;
if(ans<0){
ans+=P;
}
BreakT:;
printf("%d",ans);
return 0;
}
矩形
给出一个 n × n n \times n n×n 的网格,有些格子是黑色的剩下是白色的。有 m m m 次询问,每次询问网格中有多少 a a a 行 b b b 列的矩形,满足它的四条边上所有格子都是黑的。
1
⩽
n
,
m
⩽
1500
1 \leqslant n,m \leqslant 1500
1⩽n,m⩽1500
时间限制5s,空间限制128MB
对询问离线,枚举矩形的上边界,再从小到大枚举矩形下边界,用bitset处理列方向上的限制。对于行方向的限制预处理bitset倍增数组,每次询问可以倍增地表示出一段
1
1
1。
时间复杂度
O
(
m
n
2
log
2
n
)
O(mn^2 \log_2n)
O(mn2log2n),空间复杂度
O
(
n
2
log
2
n
+
m
)
O(n^2 \log_2n+m)
O(n2log2n+m)
#include<stdio.h>
#include<bitset>
#include<vector>
using namespace std;
#define R register int
#define I inline
char s[1500][1501];
int ans[1500];
struct Query{
int Id,Qb;
};
I auto Pair(int d,int b){
Query res;
res.Id=d;
res.Qb=b;
return res;
}
vector<Query>Q[1500];
bitset<1500>F[1500][11];
int main(){
int n,m,a,b;
scanf("%d%d",&n,&m);
for(R i=0;i!=n;i++){
scanf("%s",s[i]);
for(R j=0;j!=n;j++){
F[i][0][j]=s[i][j]=='1';
}
for(R j=1;1<<j<=n;j++){
F[i][j]=F[i][j-1]&F[i][j-1]>>(1<<j-1);
}
}
for(R i=0;i!=m;i++){
scanf("%d%d",&a,&b);
Q[a-1].push_back(Pair(i,b));
}
for(R i=0;i!=n;i++){
bitset<1500>C;
for(R j=0;j!=n;j++){
C[j]=1;
}
for(R j=i;j!=n;j++){
C&=F[j][0];
for(auto T:Q[j-i]){
b=T.Qb-1;
a=1;
bitset<1500>B=F[i][0]&F[j][0];
for(R k=0;k!=11;k++){
if((b>>k&1)==1){
B&=(F[i][k]&F[j][k])>>a;
a+=1<<k;
}
}
ans[T.Id]+=(C&C>>b&B).count();
}
}
}
for(R i=0;i!=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
将倍增改为ST表,可以将时间复杂度优化到 O ( n 2 m ) O(n^2 m) O(n2m)。
o
有一个无限大的网格图,在第 0 0 0秒的时候,有 n n n 个格子会着火,每隔一秒,一个着火的格子会导致和它八连通的未着火的格子着火,定义一个着火格子的权值为它最早着火的时间,求 t t t 秒之后所有着火格子的权值和。答案对 998244353 998244353 998244353取模。
1
⩽
n
⩽
50
,
1
⩽
t
⩽
1
0
8
1 \leqslant n \leqslant 50,1 \leqslant t \leqslant 10^8
1⩽n⩽50,1⩽t⩽108
时间限制2s,空间限制512MB
首先答案可以转化为求时间
i
i
i 覆盖的面积的前缀和。对于一段没有出现新的重叠的部分,面积为对时间的二次函数,前缀和为对时间的三次函数。求对于一个时间的覆盖面积为若干矩形的面积并,可以用扫描线求。最后对每段进行拉格朗日插值即可。
时间复杂度
O
(
n
4
)
O(n^4)
O(n4),空间复杂度
O
(
n
2
)
O(n^2)
O(n2)
#include<stdio.h>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
#define R register int
#define L long long
#define I inline
#define P 998244353
I int Add(int x,const int y){
x+=y;
return x<P?x:x-P;
}
I int GetInv(int x){
int res=1,y=P-2;
while(y!=0){
if((y&1)==1){
res=(L)res*x%P;
}
x=(L)x*x%P;
y>>=1;
}
return res;
}
int px[50],py[50],distX[100],distY[100],sum[100];
I int Dis(int x,int y){
int a=px[x]-px[y],b=py[x]-py[y];
a=a<0?-a:a;
b=b<0?-b:b;
return a>b?a:b;
}
struct Item{
int Lf,Rt;
};
I Item Pair(int l,int r){
Item res;
res.Lf=l;
res.Rt=r;
return res;
}
vector<Item>Pos[100],Neg[100];
I void Modify(int l,int r,const int d){
for(R i=l;i!=r;i++){
sum[i]+=d;
}
}
I int GetSum(const int n){
int res=0;
for(R i=0;i!=n-1;i++){
if(sum[i]!=0){
res=Add(res,distX[i+1]-distX[i]);
}
}
return res;
}
I int Calc(int t,const int n){
int res=0,tx,ty,l,r,x;
for(R i=0;i!=n;i++){
distX[i<<1]=px[i]-t;
distX[i<<1|1]=px[i]+t+1;
distY[i<<1]=py[i]-t;
distY[i<<1|1]=py[i]+t+1;
}
sort(distX,distX+(n<<1));
tx=unique(distX,distX+(n<<1))-distX;
sort(distY,distY+(n<<1));
ty=unique(distY,distY+(n<<1))-distY;
for(R i=0;i!=tx;i++){
sum[i]=0;
}
for(R i=0;i!=ty;i++){
Pos[i].clear();
Neg[i].clear();
}
for(R i=0;i!=n;i++){
l=lower_bound(distX,distX+tx,px[i]-t)-distX;
r=lower_bound(distX,distX+tx,px[i]+t+1)-distX;
x=lower_bound(distY,distY+ty,py[i]-t)-distY;
Pos[x].push_back(Pair(l,r));
x=lower_bound(distY,distY+ty,py[i]+t+1)-distY;
Neg[x].push_back(Pair(l,r));
}
for(R i=0;i!=ty-1;i++){
for(Item T:Neg[i]){
Modify(T.Lf,T.Rt,-1);
}
for(Item T:Pos[i]){
Modify(T.Lf,T.Rt,1);
}
res=((L)(distY[i+1]-distY[i])*GetSum(tx)+res)%P;
}
return res;
}
I int Solve(int l,int r,const int n){
int res=0;
if(r-l<3){
for(R i=l;i<=r;i++){
res=Add(res,Calc(i,n));
}
}else{
int a[4],fl=0,fr=0;
for(R i=0;i!=4;i++){
a[i]=Calc(l+i,n);
}
for(R i=1;i!=4;i++){
a[i]=Add(a[i-1],a[i]);
}
for(R i=0;i!=4;i++){
int pl=1,pr=1,inv=1;
for(R j=0;j!=4;j++){
if(i!=j){
pl=(-1ll-j)*pl%P;
inv=(L)(i-j)*inv%P;
pr=(L)(r-l-j)*pr%P;
}
}
inv=GetInv(inv);
fl=((L)inv*pl%P*a[i]+fl)%P;
fr=((L)inv*pr%P*a[i]+fr)%P;
}
if(fl<0){
fl+=P;
}
if(fr<0){
fr+=P;
}
res=Add(fr,P-fl);
}
return res;
}
int main(){
int n,t,d,ans=0;
scanf("%d%d",&n,&t);
if(t==0){
printf("0");
return 0;
}
for(R i=0;i!=n;i++){
scanf("%d%d",px+i,py+i);
}
set<int>S;
S.insert(0);
S.insert(t+1);
for(R i=0;i!=n;i++){
for(R j=0;j!=n;j++){
d=Dis(i,j);
if(d>1&&d+1<=t<<1){
S.insert(d+1>>1);
}
}
}
vector<int>A;
for(auto T:S){
A.push_back(T);
}
for(R i=1;i!=A.size();i++){
ans=Add(ans,Solve(A[i-1],A[i]-1,n));
}
printf("%d",((1ll+t)*Calc(t,n)-ans+P)%P);
return 0;
}
Inside information
对于服务器中的数据可以用线段树合并来维护。对数据所在的服务器考虑倒着维护。线段树的下标表示这个时间可以到新的服务器。这也是个线段树合并。查询的答案即为线段树上的前缀和。
时空复杂度
O
(
(
n
+
k
)
log
2
n
)
O((n+k) \log_2 n)
O((n+k)log2n)
#include<stdio.h>
#include<vector>
using namespace std;
#define R register int
#define I inline
#define N 240001
int root[N],ans[N],ct;
struct SegmentNode{
int Ls,Rs,Sum;
}t[30000000];
I void GetNode(int&x){
ct++;
x=ct;
t[x].Ls=t[x].Rs=0;
}
I void Insert(int p1,int p2,int lf,int rt,const int x){
t[p2].Sum=t[p1].Sum+1;
if(lf!=rt){
int mid=lf+rt>>1;
if(x>mid){
GetNode(t[p2].Rs);
t[p2].Ls=t[p1].Ls;
Insert(t[p1].Rs,t[p2].Rs,mid+1,rt,x);
}else{
GetNode(t[p2].Ls);
t[p2].Rs=t[p1].Rs;
Insert(t[p1].Ls,t[p2].Ls,lf,mid,x);
}
}
}
I int Merge(int x,int y){
if(x==0||y==0){
return x|y;
}
int p;
GetNode(p);
t[p].Ls=Merge(t[x].Ls,t[y].Ls);
t[p].Rs=Merge(t[x].Rs,t[y].Rs);
t[p].Sum=t[t[p].Ls].Sum+t[t[p].Rs].Sum;
return p;
}
I bool Query(int p,int lf,int rt,const int x){
if(lf==rt){
return t[p].Sum==1;
}
int mid=lf+rt>>1;
if(x>mid){
if(t[p].Rs==0){
return false;
}
return Query(t[p].Rs,mid+1,rt,x);
}
if(t[p].Ls==0){
return false;
}
return Query(t[p].Ls,lf,mid,x);
}
I int GetSum(int p,int lf,int rt,const int r){
if(rt<=r){
return t[p].Sum;
}
int mid=lf+rt>>1,res=0;
if(r>mid&&t[p].Rs!=0){
res=GetSum(t[p].Rs,mid+1,rt,r);
}
if(t[p].Ls!=0){
res+=GetSum(t[p].Ls,lf,mid,r);
}
return res;
}
struct Item{
int QA,QB,Id;
};
I auto Pair(int a,int b,int d){
Item res;
res.QA=a;
res.QB=b;
res.Id=d;
return res;
}
vector<Item>Q;
int main(){
int n,m,a,b,x;
scanf("%d%d",&n,&m);
m+=n-1;
for(int i=1;i<=n;i++){
GetNode(root[i]);
Insert(0,root[i],1,n,i);
}
for(R i=1;i<=m;i++){
char opt;
scanf("\n%c",&opt);
scanf("%d",&a);
if(opt=='S'){
scanf("%d",&b);
root[a]=root[b]=Merge(root[a],root[b]);
Q.push_back(Pair(a,b,i));
}else if(opt=='Q'){
scanf("%d",&b);
if(Query(root[a],1,n,b)==true){
ans[i]=-1;
}else{
ans[i]=-2;
}
}else{
Q.push_back(Pair(a,0,i));
ans[i]=1;
}
}
ct=0;
for(R i=1;i<=n;i++){
GetNode(root[i]);
}
for(auto T=Q.rbegin();T!=Q.rend();T++){
if(T->QB!=0){
a=T->QA;
b=T->QB;
root[a]=root[b]=Merge(root[T->QA],root[T->QB]);
GetNode(x);
Insert(root[a],x,1,m,T->Id);
root[a]=root[b]=x;
}
}
for(auto T:Q){
if(T.QB==0){
ans[T.Id]+=GetSum(root[T.QA],1,m,T.Id);
}
}
for(R i=1;i<=m;i++){
if(ans[i]==-1){
puts("yes");
}else if(ans[i]==-2){
puts("no");
}else if(ans[i]!=0){
printf("%d\n",ans[i]);
}
}
return 0;
}