NOIP 2024 补题记录

CNOI 浙江省系列比赛补题记录(from 2022)

全部放到一处过于卡顿,故将补完的比赛单独拿出来。

树的遍历

个人认为是这套题里最难的一道。

\(d_u\) 为原树中 \(u\) 的度数。

考虑一个生成树能被哪些根生成,如果考虑去判定就会像我一样炸掉,但是举例可以发现合法的根一定构成一条从叶子到另一个叶子的链。

考虑证明,对于每个原树上的点,其周围的边在生成树上一定形成一条链,那么根就被限定在这条链的首尾两个方向,将所有信息叠加即可得到一条链。

那么考虑对每条链算贡献,此时在链上的点就被确定了首尾,而不在链上的点则被确定了首,答案即为 \(\prod_{x\notin path}(d_x-1)!\prod_{x\in path}(d_x-2)!=\prod(d_x-1)!\prod_{x\notin path}\frac{1}{d_x-1}\)

前面的东西可以提出来,问题变为:每个点有点权,每条边有 01 边权,求所有叶子到另一个叶子且经过 1 边的链的点权乘积的和。这个直接 dfs 一遍就能算出。

#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
typedef pair<int,bool> ND;
const ll mod=1e9+7;
const int N=1e5+5;
ll ksm(ll a,ll b,ll p){
    a=a%p;
    ll r=1;
    while(b){
        if(b&1)r=r*a%p;
        a=a*a%p;
        b>>=1;
    }
    return r%p;
}
ll fac[N],inv[N];
void Init(int n){
    fac[0]=1;
    rep(i,1,n)fac[i]=fac[i-1]*i%mod;
    inv[n]=ksm(fac[n],mod-2,mod);
    per(i,n,1)inv[i-1]=inv[i]*i%mod;
}
int n,k,x[N],y[N];
bool z[N];
vector<ND>g[N];
ll f[N][2],ans;
ll I(int x){
    if(!x)return 1;
    return inv[x]*fac[x-1]%mod;
}
void dfs(int u,int fa){
    f[u][0]=f[u][1]=0;
    if(g[u].size()==1){
        f[u][0]=I(g[u].size()-1);
        return ;
    }
    for(auto e:g[u]){
        if(e.first==fa)continue;
        dfs(e.first,u);
        if(e.second)ans=(ans+(f[u][0]+f[u][1])*(f[e.first][0]+f[e.first][1])%mod)%mod,f[u][1]=(f[u][1]+(f[e.first][0]+f[e.first][1])*I(g[u].size()-1)%mod)%mod;
        else ans=(ans+f[u][1]*(f[e.first][0]+f[e.first][1])%mod+f[u][0]*f[e.first][1]%mod)%mod,f[u][0]=(f[u][0]+f[e.first][0]*I(g[u].size()-1)%mod)%mod,f[u][1]=(f[u][1]+f[e.first][1]*I(g[u].size()-1)%mod)%mod;
    }
}
void slv(){
    scanf("%d %d",&n,&k);
    Init(n);
    rep(i,1,n)g[i].clr();
    rep(i,1,n-1)scanf("%d %d",&x[i],&y[i]),z[i]=0;
    rep(i,1,k){
        int x;
        scanf("%d",&x);
        z[x]=1;
    }
    rep(i,1,n-1)g[x[i]].pb({y[i],z[i]}),g[y[i]].pb({x[i],z[i]});
    if(n==2){
        puts("1");
        return ;
    }
    rep(rt,1,n){
        if(g[rt].size()>1){
            ans=0,dfs(rt,0);
            rep(i,1,n)ans=ans*fac[g[i].size()-1]%mod;
            printf("%lld\n",ans);
            return ;
        }
    }
}
void main(){
	int T=1;
	int csid=0;scanf("%d",&csid);
	scanf("%d",&T);
	while(T--)slv();
}
}
int main(){
	string __name="";
	if(__name!=""){
		freopen((__name+".in").c_str(),"r",stdin);
		freopen((__name+".out").c_str(),"w",stdout);
	}
	ax_by_c::main();
	return 0;
}

树上查询

场上想出了支配对 2log 做法但是脑抽以为 cdq 是 3log 于是没写,写了至少也能加个 16 分上个 300 了。(其实更多人没写的原因是以为支配对是 1log 的)

考虑统计每个点作为 LCA 的贡献,对每个点维护出其子树内的所有下标连续段,那么只有连续段的子串会产生贡献。

不难发现只会合并 \(O(n)\) 次,于是连续段个数是 \(O(n)\) 的,求的过程可能要带 log。(场上不知道这个叫支配对)

现在相当于有 \(O(q)\)\((l,r,k)\)\(O(n)\)\((l',r',x)\),要对每个 \((l,r)\)\(\max_{\min(r,r')-\max(l,l')+1\ge k} x\),直接分四种情况讨论 cdq 即可。

以上是场上想法,下面想一下怎么优化到 1log:

直接把最值拆了好像不好做,考虑拆一半:

  • \(r-\max(l,l')+1\ge k,r\le r'\)

注意到 \(k\le r-l+1\)

于是只需 \(r-l'+1\ge k,r\le r'\)

\(l'\le r-k+1,r\le r'\)

然后对 \(r\) 扫描线即可。

  • \(r'-\max(l,l')+1\ge k,r>r'\)

\(r'-l+1\ge k,r'-l'+1\ge k,r>r'\)

\(k+l-1\le r'<r,k\le r'-l'+1\)

然后对 \(k\) 扫描线即可。

时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
namespace ax_by_c{
const int N=5e5+5;
struct SEG{
    int tr[N*4];
    void pu(int u){
        tr[u]=max(tr[u<<1],tr[u<<1|1]);
    }
    void clr(int u,int l,int r){
        if(l==r){
            tr[u]=0;
            return ;
        }
        int mid=l+((r-l)>>1);
        clr(u<<1,l,mid),clr(u<<1|1,mid+1,r);
        pu(u);
    }
    void upd(int u,int l,int r,int p,int v){
        if(l==r){
            tr[u]=max(tr[u],v);
            return ;
        }
        int mid=l+((r-l)>>1);
        if(p<=mid)upd(u<<1,l,mid,p,v);
        else upd(u<<1|1,mid+1,r,p,v);
        pu(u);
    }
    int Q(int u,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return tr[u];
        int mid=l+((r-l)>>1),res=0;
        if(ql<=mid)res=max(res,Q(u<<1,l,mid,ql,qr));
        if(mid+1<=qr)res=max(res,Q(u<<1|1,mid+1,r,ql,qr));
        return res;
    }
}tr;
struct DSU{
    struct node{
        int fa,sz,L,R;
    }a[N];
    void Init(int n){
        for(int i=1;i<=n;i++)a[i]={i,1,i,i};
    }
    int find(int x){
        if(a[x].fa==x)return x;
        return a[x].fa=find(a[x].fa);
    }
    bool meg(int x,int y){
        x=find(x),y=find(y);
        if(x==y)return 0;
        if(a[x].sz>a[y].sz)swap(x,y);
        a[x].fa=y;
        a[y].sz+=a[x].sz;
        a[y].L=min(a[y].L,a[x].L);
        a[y].R=max(a[y].R,a[x].R);
        return 1;
    }
}dsu;
struct U{
    int l,r,x;
};
struct Q{
    int l,r,k,id;
}qs[N];
int n,q,fa[N],de[N],sz[N],sn[N],L[N],R[N],Tim,dfa[N],mxr[N],qans[N];
vector<int>g[N];
vector<U>us;
void dfs(int u){
    L[u]=++Tim;
    dfa[Tim]=u;
    sz[u]=1;
    for(auto v:g[u]){
        if(v==fa[u])continue;
        fa[v]=u;
        de[v]=de[u]+1;
        dfs(v);
        sz[u]+=sz[v];
        if(sz[v]>sz[sn[u]])sn[u]=v;
    }
    R[u]=Tim;
}
void add(int p,int x){
    int l=dsu.a[dsu.find(p)].L,r=dsu.a[dsu.find(p)].R;
    if(mxr[l]<r)mxr[l]=r,us.push_back({l,r,x});
}
void ddfs(int u){
    for(auto v:g[u]){
        if(v==fa[u]||v==sn[u])continue;
        ddfs(v);
    }
    if(sn[u])ddfs(sn[u]);
    add(u,de[u]);
    if(u!=1&&L[u]<=L[u-1]&&L[u-1]<=R[u]&&dsu.meg(u-1,u))add(u,de[u]);
    if(u!=n&&L[u]<=L[u+1]&&L[u+1]<=R[u]&&dsu.meg(u,u+1))add(u,de[u]);
    for(auto v:g[u]){
        if(v==fa[u]||v==sn[u])continue;
        for(int i=L[v];i<=R[v];i++){
            if(dfa[i]!=1&&L[u]<=L[dfa[i]-1]&&L[dfa[i]-1]<=R[u]&&dsu.meg(dfa[i]-1,dfa[i]))add(dfa[i],de[u]);
            if(dfa[i]!=n&&L[u]<=L[dfa[i]+1]&&L[dfa[i]+1]<=R[u]&&dsu.meg(dfa[i],dfa[i]+1))add(dfa[i],de[u]);
        }
    }
}
bool cmp_U_1(U x,U y){
    return x.r<y.r;
}
bool cmp_Q_1(Q x,Q y){
    return x.r<y.r;
}
bool cmp_U_2(U x,U y){
    return x.r-x.l+1<y.r-y.l+1;
}
bool cmp_Q_2(Q x,Q y){
    return x.k<y.k;
}
void main(){
    scanf("%d",&n);
    for(int i=1,u,v;i<n;i++){
        scanf("%d %d",&u,&v);
        g[u].push_back(v),g[v].push_back(u);
    }
    de[1]=1,dfs(1);
    dsu.Init(n);
    ddfs(1);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)scanf("%d %d %d",&qs[i].l,&qs[i].r,&qs[i].k),qs[i].id=i;
    sort(us.begin(),us.end(),cmp_U_1),sort(qs+1,qs+1+q,cmp_Q_1);
    tr.clr(1,1,n);
    for(int i=q,j=(int)us.size();i>=1;i--){
        while(j&&qs[i].r<=us[j-1].r)j--,tr.upd(1,1,n,us[j].l,us[j].x);
        if(1<=qs[i].r-qs[i].k+1)qans[qs[i].id]=max(qans[qs[i].id],tr.Q(1,1,n,1,qs[i].r-qs[i].k+1));
    }
    sort(us.begin(),us.end(),cmp_U_2),sort(qs+1,qs+1+q,cmp_Q_2);
    tr.clr(1,1,n);
    for(int i=q,j=(int)us.size();i>=1;i--){
        while(j&&qs[i].k<=us[j-1].r-us[j-1].l+1)j--,tr.upd(1,1,n,us[j].r,us[j].x);
        if(qs[i].k+qs[i].l-1<qs[i].r)qans[qs[i].id]=max(qans[qs[i].id],tr.Q(1,1,n,qs[i].k+qs[i].l-1,qs[i].r-1));
    }
    for(int i=1;i<=q;i++)printf("%d\n",qans[i]);
}
}
int main(){
    ax_by_c::main();
    return 0;
}
posted @ 2025-05-02 07:49  ax_by_c  阅读(14)  评论(0)    收藏  举报