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\) 对结果的贡献为
\(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;
}

浙公网安备 33010602011771号