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