2019.05.04 【NOIP提高组】模拟 A 组

JZOJ 4637 大鱼海棠

题目

有一棵根节点是1的树,选择一个点,使这个点到根节点的路径不能选择,所有点不能选择为输,问先手是否必胜


分析

如果后手能选到后手必胜的点,先手第一步就能选到,所以除非只有一个点,否则先手必胜


JZOJ 4638 第三条跑道

题目

给定一个序列a,修改形如给区间 [ l ∼ r ] [l\sim r] [lr]乘上 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=1kpici)=i=1k(pi1)pici1
然后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]=iSONs[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 ] &gt; s o n [ y ] son[x]&gt;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]+lenson[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])(xz)2
既然要让距离和变小, ( s o n [ x ] + s o n [ y ] ) ∗ ( d e p [ z ] − d e p [ x ] ) &lt; ( x 到 z 的 节 点 数 ) ∗ 2 (son[x]+son[y])*(dep[z]-dep[x])&lt;(x到z的节点数)*2 (son[x]+son[y])(dep[z]dep[x])<(xz)2,因为 d e p [ z ] − d e p [ x ] &gt; 0 dep[z]-dep[x]&gt;0 dep[z]dep[x]>0,所以 s o n [ x ] + s o n [ y ] &lt; ( x 到 z 的 节 点 数 ) ∗ 2 son[x]+son[y]&lt;(x到z的节点数)*2 son[x]+son[y]<(xz)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;
}

后续

越变越菜

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值