10.20NOIP模拟赛题解

10.20NOIP模拟赛题解

A.线段树

题意:\(T\)次询问,求长度为\(N\)的线段树第\(K\)大区间长度,\(T\le 10^4 , N\le 10^{18}\)

考虑第\(i\)层最多有\(2^i\)个节点,并且每层区间长度差不超过1,计算\(K\)所在的层数,判断是多的还是少的即可

复杂度\(O(T)\)

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T&w){char c,p=0;
    while(!isdigit(c=getchar()))if(c=='-')p=1;
    for(w=c&15;isdigit(c=getchar());w=w*10+(c&15));if(p)w=-w;
}
typedef long long ll;
ll n,k;
int main(){
    int T;read(T);
    while(T--){
        read(n),read(k);
        ll x=1ll<<__lg(k),u=n/x,d=n-u*x;
        printf("%lld\n",k-x<d?u+1:u);
    }
    return 0;
}

B.集训

题意:在\(p\)维坐标系下,你现在在\((1,1,\dots,1)\),目标地点第\(i\)维可以是1,也可以是\(c[i]\),你有\(m\)中移动方式,第\(j\)种可以在任意一维中\(a[j]\to b[j]\)\(b[j]\to a[j]\).求移动\(q\)次的方案数。\(a[i],b[i],c[i]\le50,p\le10^6,m,q\le100\)

发现每次只能移动一维,因此每一维是独立的,设\(g[i][j]\)表示走\(i\)步,到达\(j\)的方案数
\[ g[i][a[j]]+=g[i-1][b[j]],g[i][b[j]]+=g[i-1][a[j]] \]
然后再对每一维跑背包合并,设\(f[i][j]\)表示前\(i\)维,选了\(j\)个的方案数
\[ f[i][j]=\sum_{k=0}^jf[i-1][j-k]\times g[1\ or\ c[i] ][k] \]
每一维又是不同的,因此相当于再做可重复的全排列

上述公式改为
\[ f[i][j]=\sum_{k=0}^j \frac{f[i-1][j-k]\times g[1\ or\ c[i] ][k]}{k!} \]

\[ Ans=f[p][q]\times q! \]

这样复杂度是\(O(pq^2)\)的,只能得50分,考虑优化

观察数据范围发现,坐标最大只有50,因此可以按坐标分开用矩阵加速转移

但是如果直接用矩阵,复杂度是\(O(50q^3log_2p)\)还是50分

发现矩阵是循环矩阵,再把矩阵乘法复杂度优化成\(q^2\)即可AC

复杂度\(O(50q^2log_2p)\)

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
using namespace std;
const int BUFSIZE=1e6;
char inbuf[BUFSIZE],*si=inbuf,*ti=inbuf;
struct FastIO{
    #define gc() (si==ti&&(ti=inbuf+fread(si=inbuf,1,BUFSIZE,stdin),si==ti)?EOF:*si++)
    template<typename T>inline T&rd(T&w){char c,p=0;
        while(isspace(c=gc()));if(c=='-')p=1,c=gc();
        for(w=c&15;isdigit(c=gc());w=w*10+(c&15));
        if(p)w=-w;return w;}
    inline int read(){int x;return rd(x);}

}io;
#define read io.read
typedef unsigned long long ull;
inline char smin(int&x,const int&y){return x>y?x=y,1:0;}
inline char smax(int&x,const int&y){return x<y?x=y,1:0;}
const int N=1e6+5,P=998244353;
int n,m,p,q,a[103],b[103],g[103][103],fac[103],inv[103],cnt[103];
inline void inc(int&x,int y){x+=y;if(x>=P)x-=P;}
inline int fpow(int x,int k){int r=1;for(;k;k>>=1,x=1ll*x*x%P)if(k&1)r=1ll*x*r%P;return r;}
int v[103],f[103],t[103];
const ull limit=1e19;
ull tmp;
inline void mulself(){
    memset(t,0,sizeof t);
    REP(i,0,q){
        tmp=0;
        REP(j,0,i)if((tmp+=1ll*v[j]*v[i-j])>=limit)tmp%=P;
        t[i]=tmp%P;
    }
    memcpy(v,t,sizeof v);
}
inline void mul(){
    memset(t,0,sizeof t);
    REP(i,0,q){
        tmp=0;
        REP(j,0,i)if((tmp+=1ll*f[j]*v[i-j])>=limit)tmp%=P;
        t[i]=tmp%P;
    }   
    memcpy(f,t,sizeof f);
}
int main(){
    n=read(),m=read(),p=read(),q=read();fac[0]=inv[0]=1;
    REP(i,1,p)++cnt[read()];
    REP(i,1,q)fac[i]=1ll*fac[i-1]*i%P;inv[q]=fpow(fac[q],P-2);
    for(int i=q-1;i;--i)inv[i]=1ll*inv[i+1]*(i+1)%P;
    REP(i,1,m)a[i]=read(),b[i]=read();
    g[0][1]=1;
    REP(i,1,q)REP(j,1,m)inc(g[i][a[j]],g[i-1][b[j]]),inc(g[i][b[j]],g[i-1][a[j]]);
    int L=1,now=0;
    f[0]=1;
    REP(i,1,n)if(cnt[i]){
        memset(v,0,sizeof v);
        REP(k,0,q)v[k]=1ll*(g[k][1]+(i!=1?g[k][i]:0))*inv[k]%P;
        for(int k=cnt[i];k;k>>=1,mulself())if(k&1)mul();
    }
    cout<<1ll*f[q]*fac[q]%P;
    return 0;
}

C.las

题意:给定一棵树,每个节点有个权值,每次删除一条边并输出删除前这条边所在联通块的权值

联通块的权值=\(\sum_i v_i\times i\)其中\(v_i\)表示第\(i\)大的权值

数据结构题。

时间倒流,然后加边线段树合并即可

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
using namespace std;
inline int read(){char c,p=0;int w;
    while(!isdigit(c=getchar()))if(c=='-')p=1;
    for(w=c&15;isdigit(c=getchar());w=w*10+(c&15));return p?-w:w;
}
inline char smin(int&x,const int&y){return x>y?x=y,1:0;}
inline char smax(int&x,const int&y){return x<y?x=y,1:0;}
const int N=5e5+5,p=998244353;
inline void inc(int&x,int y){x+=y;if(x>=p)x-=p;}
int n,m,a[N],b[N],rt[N],cnt;
struct node{int ls,rs,cnt,las,sum;}t[N*40];
inline void pushup(int o){
    t[o].las=(1ll*t[t[o].ls].cnt*t[t[o].rs].sum%p+t[t[o].ls].las+t[t[o].rs].las)%p;
}
inline void ins(int&o,int l,int r,int x){
    if(!o)o=++cnt;++t[o].cnt;inc(t[o].sum,x);
    if(l==r)return inc(t[o].las,1ll*x*t[o].cnt%p);int mid=l+r>>1;
    x<=mid?ins(t[o].ls,l,mid,x):ins(t[o].rs,mid+1,r,x);
    pushup(o);
}
inline void merge(int&x,int y,int l,int r){
    if(!x||!y){x|=y;return;}int mid=l+r>>1;
    merge(t[x].ls,t[y].ls,l,mid);
    merge(t[x].rs,t[y].rs,mid+1,r);
    t[x].cnt+=t[y].cnt,inc(t[x].sum,t[y].sum);
    if(l==r)t[x].las=1ll*t[x].cnt*(t[x].cnt+1)/2*l%p;
    else pushup(x);
}
int fa[N],ans[N],e[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int main(){
    n=read(),m=read();
    REP(i,1,n)ins(rt[i],-m,m,read()),fa[i]=i;
    REP(i,1,n-1)a[i]=read(),b[i]=read();
    REP(i,1,n-1)e[i]=read();
    for(int i=n-1;i;--i){
        int x=find(a[e[i]]),y=find(b[e[i]]);
        if(x!=y)merge(rt[x],rt[y],-m,m);fa[y]=x;
        ans[i]=t[rt[x]].las;
    }
    REP(i,1,n-1)printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/HolyK/p/9825261.html