Loading

20260411 模拟赛

题目

T1

先合并区间,使它们两两之间都有空位。

合并区间可以先按 \(l\) 排序,当发现 \(l_{i+1}\le r_i+1\) 就将它们合并,并更新合并后的区间的 \(r\)

对于区间 \([l,r]\),只要跳跃能力大于 \(r-l+1\) 就可以越过。

去掉区间内的道具,然后将区间和道具按位置排序。

考虑反悔贪心,一开始不选道具,当发现越不过区间时再在已经走过的道具中从大往小选,可以使用优先队列维护。

#include<bits/stdc++.h>
#define N 100005
#define getc getchar
using namespace std;
int n,m,L;
struct line{
	int l,r;
	bool operator<(const line &x){return l<x.l;}
}a[N],b[N];
priority_queue<int>q;
void read(int &x){
	char c=getc();x=0;
	while(c<'0'||c>'9') c=getc();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getc();
}
int main(){
	int T;read(T);while(T--){
		read(n),read(m),read(L);
		for(int i=1;i<=n;i++) read(a[i].l),read(a[i].r);
		for(int i=1;i<=m;i++) read(b[i].l),read(b[i].r);
		sort(a+1,a+n+1),sort(b+1,b+m+1);
		int k=0,ans=0;
		for(int i=1;i<=n;i++){
			a[++k]=a[i];
			while(i<n&&a[i+1].l<=a[k].r+1)
				a[k].r=max(a[k].r,a[++i].r);
		}n=k;
		for(int i=1,j=1,v=1;~ans&&i<=n;i++){
			while(j<=m&&b[j].l<a[i].l) q.push(b[j++].r);
			while(v<=a[i].r-a[i].l+1){
				if(q.empty()){ans=-1;break;}
				v+=q.top(),q.pop(),ans++;
			}while(j<=m&&a[i].l<=b[j].l&&a[i].r>=b[j].l) j++;
		}while(!q.empty()) q.pop();
		printf("%d\n",ans);
	}
	return 0;
}

T2

发现一个性质,若可以在两个分割成 \(x,y\) 的环上动,只会在每 \(\dfrac{1}{\gcd(x,y)}\) 处有阻隔,而中间可以任意穿行。

进一步的,若有 \(k\) 个分割成 \(a_1,a_2,\dots,a_k\) 的环,则阻隔每 \(\dfrac{1}{\gcd(a_1,a_2,\dots,a_k)}\) 出现一次。

询问两个 \((i_x,j_x),(i_y,j_y)\) 是否连通等价于:

\(p=\gcd(a_{i_x},a_{i_x+1},\dots,a_{i_y})\)\(\lceil\dfrac{j_xp}{a_{i_x}}\rceil=\lceil\dfrac{j_yp}{a_{i_y}}\rceil\)

解释:

在有 \(a_i\) 个位置的环上阻隔将其分成了 \(p\) 个区域,每个区域有 \(\dfrac{a_i}{p}\) 块,而环上的第 \(k\) 块在其中第 \(\lceil\dfrac{k}{a_i\div p}\rceil=\lceil\dfrac{kp}{a_i}\rceil\) 个区域,连通当且仅当它们所在区域相同。

问题转化为维护带修区间 \(\gcd\)

\(\gcd\) 有可合并性,可以用线段树维护。

时间复杂度 \(\mathcal O(n\log^2 n)\),但有一个是求 \(\gcd\)\(\log\) 跑得不是很满,加上时限 1.5s 可以通过。

#include<bits/stdc++.h>
#define N 1000005
#define getc getchar
using namespace std;
int n,m,a[N];
void read(int &x){
	char c=getc();x=0;
	while(c<'0'||c>'9') c=getc();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getc();
}
class SGT{
	#define l(i) ((i)<<1)
	#define r(i) ((i)<<1|1)
	private:int tr[N<<2];
	public:
		void upd(int p,int v,int x=1,int l=1,int r=n){
			if(l==r) return tr[x]=v,void();
			int mid=l+r>>1;
			if(p<=mid) upd(p,v,l(x),l,mid);
			else upd(p,v,r(x),mid+1,r);
			tr[x]=__gcd(tr[l(x)],tr[r(x)]);
		}
		int query(int ql,int qr,int x=1,int l=1,int r=n){
			if(ql<=l&&qr>=r) return tr[x];
			int mid=l+r>>1,res=0;
			if(ql<=mid) res=__gcd(res,query(ql,qr,l(x),l,mid));
			if(qr>mid) res=__gcd(res,query(ql,qr,r(x),mid+1,r));
			return res;
		}
}tr;
int main(){
	int T;read(T);while(T--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			scanf("%d",a+i),tr.upd(i,a[i]);
		while(m--){
			int op;read(op);
			if(op==1){
				int ix,jx,iy,jy;
				read(ix),read(jx),read(iy),read(jy);
				int p=tr.query(min(ix,iy),max(ix,iy));
				puts((1ll*jx*p+a[ix]-1)/a[ix]^(1ll*jy*p+a[iy]-1)/a[iy]?"No":"Yes");
			}
			else{
				int k,v;
				scanf("%d%d",&k,&v);
				tr.upd(k,a[k]=v);
			}
		}
	}
	return 0;
}

T3

对于 \(f(x)\) 考虑每个点会被作为 \(\operatorname{LCA}\) 统计几次。

一个点 \(x\) 要作为 \(\operatorname{LCA}\),充要条件是选的 \(k\) 个点都在 \(x\) 的子树内且不全在一个儿子的子树内。

这个是好求的,用总方案减去全在一个儿子的子树内的方案即可。

具体来说,设 \(x\) 的子树大小为 \(siz_x\),则\(x\) 对结果的贡献为

\[(\dbinom{siz_x}{k}-\sum_{p\in son_x} \binom{siz_p}{k})x \]

\(f\) 的值就是它们全部相加。

考虑换根 DP,设求出了 \(f(x)\) 的值,要转移到 \(p\in son_x\)\(f(p)\)

对于子树大小的变化为 \(siz'_x=n-siz_p,siz_p=n\)

变化 \(siz\) 的点只有 \(x,p\),可以直接改变它们的贡献,预处理阶乘、阶乘逆求组合数可以做到 \(\mathcal O(n)\)

#include<bits/stdc++.h>
#define N 200005
#define getc getchar
using namespace std;
vector<int>s[N];
const int P=998244353;
int n,k,ans,siz[N],fac[N],ifac[N];
void read(int &x){
	char c=getc();x=0;
	while(c<'0'||c>'9') c=getc();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getc();
}
int inv(int x){return x^1?1ll*(P-P/x)*inv(P%x)%P:x;}
int C(int x,int y){return y>x?0:1ll*fac[x]*ifac[y]%P*ifac[x-y]%P;}
int dfs(int x,int fa){
	int res=0;siz[x]=1;
	for(auto p:s[x]) if(p^fa)
		(res+=dfs(p,x))%=P,siz[x]+=siz[p],
		res=(res-1ll*C(siz[p],k)*x)%P;
	return (res+1ll*C(siz[x],k)*x)%P;
}
void DP(int x,int fa,int v){
	(ans+=v)%=P;
	for(auto p:s[x]) if(p^fa){
		int t=v;
		t=(t-1ll*C(n,k)*(x-p))%P;
		t=(t+1ll*C(siz[p],k)*(x-p))%P;
		t=(t+1ll*C(n-siz[p],k)*(x-p))%P;
		DP(p,x,t);
	}
}
int main(){
	int T;read(T);fac[0]=1;for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%P;
	ifac[N-1]=inv(fac[N-1]);for(int i=N-1;i;i--) ifac[i-1]=1ll*ifac[i]*i%P;
	while(T--){
		read(n),read(k),ans=0;
		for(int i=1;i<=n;i++) s[i].clear();
		for(int i=1,u,v;i<n;i++)
			read(u),read(v),
			s[u].emplace_back(v),
			s[v].emplace_back(u);
		DP(1,0,dfs(1,0)),printf("%d\n",(ans+P)%P);
	}
	return 0;
}
posted @ 2026-04-11 16:24  Jokersen  阅读(7)  评论(0)    收藏  举报