noip2026模拟赛01题解

莫春者(muchun)

考虑拆贡献。由于 \(\sum_{i=1}^n\deg_i=2m\),所以不同的 \(\deg\) 种类只有至多 \(\sqrt{2m}\) 种。于是统计每种 \(\deg\) 出现的次数,枚举 \(\deg\) 计算即可,时间复杂度 \(O(m)\)

忆追(llacer)

走一条边实际上就是增加或减少一个素因子。于是可以先把结论猜出来:即从 \(u\) 变到 \(v\) ,需经过 \(\gcd(u,v)\) 中转。也就是 \(u \to \gcd(u,v) \to v\) 。走的过程中只要不走回头路就能保证路径最短。

接下来计数即可,考虑 \(u \to \gcd(u,v)\) 的路径,就等价于 \(\frac{u}{\gcd(u,v)}\to 1\) 的路径。可以通过调整每次走哪个素因子来得到不同的最短路走法,问题转化为 \(\frac{u}{\gcd(u,v)}\) 的素因子的可重排列。由对称性, \(\gcd(u,v) \to v\) 的处理同理。最后两边方案乘起来。

实现的时候,一开始就把质因数做预处理,并且只需要分解 \(D\) 的素因数。因为 \(u,v\) 一定是 \(D\) 的因数,所以 \(u,v\) 的素因数一定是 \(D\) 的素因数。可以用 \(O(n^{\frac{1}{3}})\) 大概估计一个数 \(n\) 的因数个数,而素因数个数一定更小,所以 \(D\) 的素因数个数一定远远小于 \(10^5\)

繁殖菌(mushroom)

先将字符串离散化成数字,然后就转化为了数颜色问题,这个问题在序列上做是简单的。

接下来想如何把树上问题转化为序列上的的问题。由于问的节点是跟深度有关,所以我们考虑先处理出树的 bfs 序。因为在 bfs 序上,同一深度的点是连续的。所以询问就变成了序列上区间数颜色的问题,用莫队解决就好了。

然后想如何快速的定位区间,我们需要在至多 \(O(\log^2 n)\) 的时间内定位出查询区间,容易想到一种暴力求法,就是在 bfs 的时候记录下每一个深度的起始位置和结束位置,对于需要查询的深度,从起始位置开始,找到第一个属于子树 \(u\)\(v\) ;从结束位置开始,找到最后一个属于子树 \(u\)\(v\)。这样定位显然是会超时的。

怎么优化呢?想一想,我们的询问除了深度的约束,还有子树的约束,而一棵子树中所有的点在 dfs 序下是连续的。于是可以这样做,求一遍 dfs 序,对于同一深度的节点,用同一个容器来维护他们的 dfs 序(比如存在一个 vector 里),同时记录每棵子树第一个 dfs 到的元素的 dfs 序和最后一个的 dfs 序。二分定位即可。单次定位复杂度 \(O(\log n)\)

之后就是莫队了。在 \(n,m\) 同阶的情况下,时间复杂度为 \(O(n \log n + n \sqrt n)\)

执念之地(cling)

事实上 \(l\) 并没有用,可以将区间缩成 \([0,1]\) 来考虑,最后答案乘上 \(l\) 即可

考虑一个点 \(x\) 被至少 \(k\) 条直线覆盖的概率,是

\[p(x)=\sum_{i=k}^n {n \choose i} (2x(1-x))^i(1-2x(1-x))^{n-i} \]

于是答案就是对其积分:

\[\int_0^1p(x)dx=\int_0^1\sum_{i=k}^n {n \choose i} (2x(1-x))^i(1-2x(1-x))^{n-i} dx \]

\((1-2x(1-x))^{n-i}\) 做二项式展开,得

\[\sum_{j=0}^{n-i} {n-i\choose j} (-1)^j(2x(1-x))^j \]

代回原式,交换求和与积分顺序得

\[\sum_{i=k}^n {n \choose i}\sum_{j=0}^{n-i} {n-i\choose j} (-1)^j\int_0^1 (2x(1-x))^i(2x(1-x))^j dx \]

整理积分部分得

\[\int_0^1 (2x(1-x))^i(2x(1-x))^jdx=2^{i+j}\int_0^1 x^{i+j}(1-x)^{i+j}dx \]

注意到这是第一类欧拉积分(贝塔积分)的形式,化得

\[\int_0^1 x^{i+j}(1-x)^{i+j}dx=B(i+j+1,i+j+1) \]

\[B(x,y)=\frac{(x-1)!(y-1)!}{(x+y-1)!} \]

代回原式得

\[\sum_{i=k}^n \sum_{j=0}^{n-i} {n \choose i}{n-i\choose j} (-1)^j2^{i+j}B(i+j+1,i+j+1) \]

此时已经可以 \(O(n^2)\) 做。但对于此题 \(n \le 10^5\) 的部分,考虑继续优化。

注意到

\[{n \choose i}{n-i\choose j}={n\choose i+j}{i+j\choose j} \]

所以可以考虑改为枚举 \(i+j\),则求和式子变为

\[\sum_{i=k}^n {n\choose i}2^iB(i+1,i+1) \sum_{j=0}^{i-k} (-1)^j{i\choose j} \]

化简后面的求和

\[\sum_{j=0}^{i-k} (-1)^j{i\choose j} \\=\sum_{j=0}^{i-k} (-1)^j\Bigg({i-1\choose j}+{i-1\choose j-1} \Bigg) \\=\sum_{j=0}^{i-k} (-1)^j{i-1\choose j}-\sum_{j=0}^{i-k-1} (-1)^j{i-1\choose j} \\=(-1)^{i-k}{i-1\choose i-k} \]

于是,我们得到了一个可以 \(O(n)\) 算的式子

\[\sum_{i=k}^n {n\choose i}{i-1\choose i-k}(-1)^{i-k}2^iB(i+1,i+1) \]

预处理阶乘、逆元、\(2\) 的若干次幂,时间复杂度线性。
注意最后答案要乘上一开始忽略的 \(l\)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define int long long
using namespace std;
const int N=2e5+10,mod=1e9+7;
int n,m,tot,ans;

vector<int> g[N];
int a[N],cnt[N],du[N];

map <int,int> mp;

int f(int x,int y){
	return (x^y)*(x|y)%mod*(x&y)%mod; 
} 

signed main(){
	freopen("muchun.in","r",stdin);
	freopen("muchun.out","w",stdout); 
	scanf("%lld%lld",&n,&m);
	while(m--){
		int u,v;
		scanf("%lld%lld",&u,&v);
		du[u]++,du[v]++;
	}
	rep(i,1,n){
		if(mp.find(du[i])==mp.end()){
			tot++;
			mp[du[i]]=tot;
			cnt[tot]++;
			a[tot]=du[i];
		}
		else cnt[mp[du[i]]]++;
	} 
	rep(i,1,tot) rep(j,i+1,tot)	ans=(ans+f(a[i],a[j])*(cnt[i]*cnt[j]%mod)%mod)%mod;
	cout<<ans;
	return 0;
}
#include <bits/stdc++.h>
#define mp make_pair
#define gcd __gcd 
#define int long long
#define itt int  
#define rep(i,a,b) for(int i=(a);i<=(b);i++) 
#define repp(i,a,b,c) for(int i=(a);i<=(b);i+=(c)) 
#define per(i,a,b) for(int i=(a);i>=(b);i--) 
using namespace std;

const int N=3e5+10,mod=998244353;

itt d,n,q,cnt,cnt1[N],cnt2[N];
int fact[N/100]={1},inv[N/100]={1},prime[N];

int ksm(int a,int b){
	int s=1;
	while(b){
		if(b&1) s=a*s%mod;
		b>>=1,a=a*a%mod;
	}
	return s%mod;
}

signed main(){
    freopen("llacer.in","r",stdin);
    freopen("llacer.out","w",stdout);
	cin>>d>>q;
	rep(i,1,100) fact[i]=fact[i-1]*i%mod;
	inv[100]=ksm(fact[100],mod-2);
	per(i,99,1) inv[i]=inv[i+1]*(i+1)%mod; 
	int tmp=d; 
	rep(i,2,d/i+1){
		if(tmp%i==0) prime[++cnt]=i;
		while(tmp%i==0) tmp/=i;
	}
	if(tmp>1) prime[++cnt]=tmp;
	rep(cas,1,q){
		int u,v,x;
		scanf("%lld%lld",&u,&v);
		x=gcd(u,v);
		u/=x,v/=x;
		int I=0,J=0,U=u,V=v,i=0,j=0;
		rep(y,1,cnt){
			int k=prime[y];
			if(u%k==0) i++;
			while(u%k==0){
				I++; 
				cnt1[i]++;
				u/=k;
			}
		}
		rep(y,1,cnt){
			int k=prime[y];
			if(v%k==0) j++;
			while(v%k==0){
				J++;
				cnt2[j]++;
				v/=k;
			}
		}
		int ans1=fact[I],ans2=fact[J];
		rep(k,1,i) ans1=ans1*inv[cnt1[k]]%mod;
		rep(k,1,j) ans2=ans2*inv[cnt2[k]]%mod;
		printf("%lld\n",ans1*ans2%mod);
		rep(k,1,i) cnt1[k]=0;
		rep(k,1,j) cnt2[k]=0; 
	}
	return 0;
}
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mid (l+r)/2
using namespace std;
const int N=1e5+10;
int n,Q,root,max_col,max_dep,m,res,timer;
int a[N],col[N],dep[N],sta[N],ed[N],cnt[N],dfn[N],out[N];
struct query{
	int L,R,ans,id,b;
}qu[N];

vector <int> g[N],b[N];
map <string,int> mp;

void dfs(int u,int fa){
	dfn[u]=++timer;
	dep[u]=dep[fa]+1;
	if(b[dep[u]].size()==0) b[dep[u]].push_back(-1);
	b[dep[u]].push_back(dfn[u]);
	for(auto v:g[u]) dfs(v,u);
	out[u]=timer;
	return ;
}

void bfs(int st){
	queue <int> q;
	stack <int> s;
	q.push(st);
	while(!q.empty()){
		int u=q.front();
		max_dep=max(max_dep,dep[u]);
		a[++m]=u;
		if(!sta[dep[u]]) sta[dep[u]]=m;
		if(!ed[dep[u]-1]) ed[dep[u]-1]=m-1;
		for(auto v:g[u]) q.push(v);
		q.pop();
	}
	ed[max_dep]=m;
}

bool cmp1(query A,query B){
	if(A.L!=B.L) return A.L<B.L;
	else return A.b<B.b;
}

bool cmp2(query A,query B){
	return A.id<B.id;
}

void add(int x){
	cnt[a[x]]++;
	if(cnt[a[x]]==1) res++;
}

void del(int x){
	cnt[a[x]]--;
	if(cnt[a[x]]==0) res--;
}

int getL(int z,int u){
	return lower_bound(b[z].begin()+1,b[z].end(),dfn[u])-b[z].begin()-1;
}

int getR(int z,int u){
	return upper_bound(b[z].begin()+1,b[z].end(),out[u])-b[z].begin()-2;
}

int main(){
    freopen("mushroom.in","r",stdin);
    freopen("mushroom.out","w",stdout);
	scanf("%d",&n);
	int len=sqrt(n);
	rep(i,1,n){
		string s;
		int fa;
		cin>>s;
		scanf("%d",&fa);
		if(mp.find(s)==mp.end()) col[i]=++max_col,mp[s]=max_col;
		else col[i]=mp[s];
		g[fa].push_back(i);
	}
	col[0]=++max_col;	
	dfs(root,n+1);bfs(root);
	scanf("%d",&Q);
	rep(i,1,Q){
		int u,k,z;
		scanf("%d%d",&u,&k);
		qu[i].id=i,z=dep[u]+k;
		qu[i].L=sta[z]+getL(z,u);
		qu[i].R=sta[z]+getR(z,u);
		qu[i].b=(qu[i].R-1+len)/len;
	}
	sort(qu+1,qu+Q+1,cmp1);
	int l=0,r=0;
	rep(i,1,n+1)a[i]=col[a[i]];
	rep(i,1,Q){
		while(r<qu[i].R) add(++r);
		while(l>qu[i].L) add(--l);
		while(l<qu[i].L) del(l++);
		while(r>qu[i].R) del(r--);
		qu[i].ans=res;
	}
	sort(qu+1,qu+Q+1,cmp2);
	rep(i,1,Q) printf("%d\n",qu[i].ans);
	return 0;
} 
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define int long long
using namespace std;
const int N=2e5+10,mod=998244353;

int pw[N]={1},fact[N]={1},inv[N]={1};

int ksm(int a,int b){
	int s=1;
	while(b){
		if(b&1) s=a*s%mod;
		a=a*a%mod;
		b>>=1;
	}
	return s%mod;
}

void pre(int n){
	rep(i,1,n){
		fact[i]=fact[i-1]*i%mod;
		pw[i]=pw[i-1]*2%mod;
	}
	inv[n]=ksm(fact[n],mod-2);
	per(i,n-1,1) inv[i]=inv[i+1]*(i+1)%mod; 
}

int c(int n,int m){
	return fact[n]*inv[m]%mod*inv[n-m]%mod;
}

signed main(){
    freopen("cling.in","r",stdin);
    freopen("cling.out","w",stdout);
	int n,k,l,ans=0;
	pre(2e5+5);
	cin>>n>>k>>l;
	rep(i,k,n){
		int flag;
		if((i-k)%2) flag=-1ll;
		else flag=1ll;
		ans=(ans+flag*c(n,i)*c(i-1,i-k)%mod*pw[i]%mod*fact[i]%mod*fact[i]%mod*inv[2*i+1]%mod+mod)%mod;
	}
	cout<<ans*(l%mod)%mod;
	return 0;
}
posted @ 2026-07-24 09:39  shaotianyu  阅读(12)  评论(0)    收藏  举报