1. 程式人生 > >10.20NOIP模擬賽題解

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;
}