JZOJ 4637 大鱼海棠
题目
有一棵根节点是1的树,选择一个点,使这个点到根节点的路径不能选择,所有点不能选择为输,问先手是否必胜
分析
如果后手能选到后手必胜的点,先手第一步就能选到,所以除非只有一个点,否则先手必胜
JZOJ 4638 第三条跑道
题目
给定一个序列a,修改形如给区间 [ l ∼ r ] [l\sim r] [l∼r]乘上 x x x,查询形如询问 ∏ i = l r φ ( a i ) \prod_{i=l}^r\varphi(a_i) ∏i=lrφ(ai)
分析
根据欧拉函数的定义,
φ
(
∏
i
=
1
k
p
i
c
i
)
=
∏
i
=
1
k
(
p
i
−
1
)
p
i
c
i
−
1
\varphi(\prod_{i=1}^kp_i^{c_i})=\prod_{i=1}^k(p_i-1)p_i^{c_i-1}
φ(i=1∏kpici)=i=1∏k(pi−1)pici−1
然后109棵线段树解决此题
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
typedef unsigned uit;
const uit prime[111]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599};
const uit N=10011,mod=100000007; uit n,a[N];
inline uit iut(){
rr uit ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline uit ksm(uit x,uit y){
rr uit ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
inline void print(uit ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
struct Segt{
uit w[N<<2],lazy[N<<2];
inline void build(uit k,uit l,uit r,uit x){
if (l==r){w[k]=!(a[l]%x); return;}
rr uit mid=(l+r)>>1;
build(k<<1,l,mid,x);
build(k<<1|1,mid+1,r,x);
w[k]=w[k<<1]+w[k<<1|1];
}
inline void pushdown(uit k,uit l,uit r,uit mid){
if (!lazy[k]) return;
w[k<<1]=mid-l+1,w[k<<1|1]=r-mid,
lazy[k<<1]=lazy[k<<1|1]=1,lazy[k]=0;
}
inline signed query(uit k,uit l,uit r,uit x,uit y){
if (l==x&&r==y) return w[k];
rr uit mid=(l+r)>>1; pushdown(k,l,r,mid);
if (y<=mid) return query(k<<1,l,mid,x,y);
else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
else return query(k<<1,l,mid,x,mid)+query(k<<1|1,mid+1,r,mid+1,y);
w[k]=w[k<<1]+w[k<<1|1];
}
inline void update(uit k,uit l,uit r,uit x,uit y){
if (l==x&&r==y){lazy[k]=1,w[k]=r-l+1; return;}
rr uit mid=(l+r)>>1; pushdown(k,l,r,mid);
if (y<=mid) update(k<<1,l,mid,x,y);
else if (x>mid) update(k<<1|1,mid+1,r,x,y);
else update(k<<1,l,mid,x,mid),update(k<<1|1,mid+1,r,mid+1,y);
w[k]=w[k<<1]+w[k<<1|1];
}
}Tre[111];
struct segt{
uit w[N<<2],lazy[N<<2];
inline void build(uit k,uit l,uit r){
lazy[k]=1;
if (l==r){w[k]=a[l]; return;}
rr uit mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
w[k]=1ll*w[k<<1]*w[k<<1|1]%mod;
}
inline void pushdown(uit k,uit l,uit r,uit mid){
if (lazy[k]==1) return;
rr uit len1=mid-l+1,len2=r-mid;
w[k<<1]=1ll*w[k<<1]*ksm(lazy[k],len1)%mod,
w[k<<1|1]=1ll*w[k<<1|1]*ksm(lazy[k],len2)%mod,
lazy[k<<1]=1ll*lazy[k<<1]*lazy[k]%mod,
lazy[k<<1|1]=1ll*lazy[k<<1|1]*lazy[k]%mod,
lazy[k]=1;
}
inline signed query(uit k,uit l,uit r,uit x,uit y){
if (l==x&&r==y) return w[k];
rr uit mid=(l+r)>>1; pushdown(k,l,r,mid);
if (y<=mid) return query(k<<1,l,mid,x,y);
else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
else return 1ll*query(k<<1,l,mid,x,mid)*query(k<<1|1,mid+1,r,mid+1,y)%mod;
w[k]=1ll*w[k<<1]*w[k<<1|1]%mod;
}
inline void update(uit k,uit l,uit r,uit x,uit y,uit z){
if (l==x&&r==y){
lazy[k]=1ll*lazy[k]*z%mod,
w[k]=1ll*w[k]*ksm(z,r-l+1)%mod;
return;
}
rr uit mid=(l+r)>>1; pushdown(k,l,r,mid);
if (y<=mid) update(k<<1,l,mid,x,y,z);
else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
w[k]=1ll*w[k<<1]*w[k<<1|1]%mod;
}
}tre;
signed main(){
n=iut();
for (rr uit i=1;i<=n;++i) a[i]=iut();
tre.build(1,1,n);
for (rr uit i=1;i<110;++i) Tre[i].build(1,1,n,prime[i]);
for (rr uit m=iut();m;--m){
rr uit q=iut(),l=iut(),r=iut();
if (q){
rr uit ans1=1,ans2=1;
for (rr uit i=1;i<110;++i){
rr uit cnt=Tre[i].query(1,1,n,l,r);
ans1=1ll*ans1*ksm(prime[i]-1,cnt)%mod,
ans2=1ll*ans2*ksm(prime[i],cnt)%mod;
}
ans1=1ll*ans1*tre.query(1,1,n,l,r)%mod,
ans1=1ll*ans1*ksm(ans2,mod-2)%mod;
print(ans1); putchar(10);
}
else{
rr uit x=iut();
for (rr uit i=1;i<110;++i)
if (!(x%prime[i]))
Tre[i].update(1,1,n,l,r);
tre.update(1,1,n,l,r,x);
}
}
return 0;
}
JZOJ 4639 Angel Beats!
题目
多组询问在一棵树上两个点 x , y x,y x,y问能否找到一个点使以 x x x为根的子树和以 y y y为根的子树到该点的距离和最短
分析
毋庸置疑,答案肯定在
x
x
x到
y
y
y的链上或以
x
,
y
x,y
x,y为根的子树上,那就分类讨论
设
s
[
x
]
s[x]
s[x]表示以
x
x
x为根的子树到
x
x
x的距离和,
s
[
x
]
=
∑
i
∈
S
O
N
s
[
i
]
+
s
o
n
[
i
]
s[x]=\sum_{i\in SON}s[i]+son[i]
s[x]=∑i∈SONs[i]+son[i];
l
e
n
len
len为
x
x
x到
y
y
y的距离,
l
e
n
=
d
e
p
[
x
]
+
d
e
p
[
y
]
−
(
d
e
p
[
l
c
a
]
∗
2
)
len=dep[x]+dep[y]-(dep[lca]*2)
len=dep[x]+dep[y]−(dep[lca]∗2)
若答案
z
z
z在
x
,
y
x,y
x,y的链上,
a
n
s
=
s
[
x
]
+
s
[
y
]
+
s
o
n
[
x
]
∗
(
d
e
p
[
x
]
−
d
e
p
[
z
]
)
+
s
o
n
[
y
]
∗
(
l
e
n
−
(
d
e
p
[
x
]
−
d
e
p
[
z
]
)
)
ans=s[x]+s[y]+son[x]*(dep[x]-dep[z])+son[y]*(len-(dep[x]-dep[z]))
ans=s[x]+s[y]+son[x]∗(dep[x]−dep[z])+son[y]∗(len−(dep[x]−dep[z]))
但是除非
s
o
n
[
x
]
=
s
o
n
[
y
]
,
a
n
s
=
s
[
x
]
+
s
[
y
]
+
s
o
n
[
x
]
∗
l
e
n
son[x]=son[y],ans=s[x]+s[y]+son[x]*len
son[x]=son[y],ans=s[x]+s[y]+son[x]∗len,否则都会掉进子树里,那又要重新说了
一般
s
o
n
[
x
]
>
s
o
n
[
y
]
son[x]>son[y]
son[x]>son[y],若答案在
x
x
x上,答案为
s
[
x
]
+
s
[
y
]
+
l
e
n
∗
s
o
n
[
y
]
s[x]+s[y]+len*son[y]
s[x]+s[y]+len∗son[y],然后如何求出以
x
x
x为子树的重心
z
z
z
那么在原来的基础上,增加
(
s
o
n
[
x
]
+
s
o
n
[
y
]
)
∗
(
d
e
p
[
z
]
−
d
e
p
[
x
]
)
−
(
x
到
z
的
节
点
数
)
∗
2
(son[x]+son[y])*(dep[z]-dep[x])-(x到z的节点数)*2
(son[x]+son[y])∗(dep[z]−dep[x])−(x到z的节点数)∗2
既然要让距离和变小,
(
s
o
n
[
x
]
+
s
o
n
[
y
]
)
∗
(
d
e
p
[
z
]
−
d
e
p
[
x
]
)
<
(
x
到
z
的
节
点
数
)
∗
2
(son[x]+son[y])*(dep[z]-dep[x])<(x到z的节点数)*2
(son[x]+son[y])∗(dep[z]−dep[x])<(x到z的节点数)∗2,因为
d
e
p
[
z
]
−
d
e
p
[
x
]
>
0
dep[z]-dep[x]>0
dep[z]−dep[x]>0,所以
s
o
n
[
x
]
+
s
o
n
[
y
]
<
(
x
到
z
的
节
点
数
)
∗
2
son[x]+son[y]<(x到z的节点数)*2
son[x]+son[y]<(x到z的节点数)∗2,可以考虑倍增,预处理重儿子,只要满足条件就往下跳,时间复杂度
O
(
(
n
+
q
)
l
o
g
n
)
O((n+q)logn)
O((n+q)logn)
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
struct node{int y,next;}e[100001];
int k,n,t,ls[100001],dep[100001],f[100001][17],dp[100001],core[100001][17],s[100001],son[100001];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void add(int x,int y){e[++k]=(node){y,ls[x]},ls[x]=k;}
inline void dfs1(int x){
dep[x]=dep[f[x][0]]+1,s[x]=0,son[x]=1;
for (rr int i=ls[x];i;i=e[i].next){
dfs1(e[i].y),son[x]+=son[e[i].y];
s[x]+=s[e[i].y]+son[e[i].y];
if (!core[x][0]||son[e[i].y]>son[core[x][0]]) core[x][0]=e[i].y;
}
if (son[x]==1) core[x][0]=x;
}
inline void dfs2(int x){
dp[x]=dp[f[x][0]]+son[x];
for (rr int i=ls[x];i;i=e[i].next) dfs2(e[i].y);
}
inline signed LCA(int x,int y){
if (x==y) return x;
for (rr int i=t;~i;--i)
if (dep[x]-(1<<i)>=dep[y]) x=f[x][i];
if (x==y) return x;
for (rr int i=t;~i;--i)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
if (x==y) return x;
else return f[x][0];
}
inline signed answ(int u,int sonx,int ans){
rr int x=u;
for (rr int i=t;~i;--i)
if ((son[core[x][i]]<<1)>sonx) x=core[x][i];
ans+=sonx*(dep[x]-dep[u])-((dp[x]-dp[u])<<1);
return ans;
}
signed main(){
n=iut();
for (rr int i=2;i<=n;++i) add(f[i][0]=iut(),i);
dfs1(1),dfs2(1); f[1][0]=1;
for (t=1;n>>t;++t); --t;
for (rr int j=1;j<=t;++j)
for (rr int i=1;i<=n;++i)
if (f[i][j-1])
f[i][j]=f[f[i][j-1]][j-1],
core[i][j]=core[core[i][j-1]][j-1];
for (rr int q=iut();q;--q){
rr int x=iut(),y=iut(),ans=0;
if (dep[x]<dep[y]) x^=y,y^=x,x^=y;
rr int lca=LCA(x,y),len=dep[x]+dep[y]-(dep[lca]<<1);
if (lca==y) ans=answ(y,son[y],s[y]);
else if (son[x]==son[y]) ans=s[x]+s[y]+len*son[x];
else{
if (son[x]<son[y]) x^=y,y^=x,x^=y;
ans=answ(x,son[x]+son[y],s[x]+s[y]+len*son[y]);
}
print(ans),putchar(10);
}
return 0;
}
后续
越变越菜