前言
- 对自己很失望,越来越不行。
- 主要是特别颓废,而且特别消极。
- 还有数据结构学傻了,别人拿数据结构优化程序我拿数据结构增加时间复杂度。
T1
- 原题。考试一直在打T3,T1就留了十分钟,凭一些模糊的印象打了个$Theta(N^2)$暴力骗了70分。
- 估计不是原题就死了吧。
- 先把区间按左端点排序,然后从小到大枚举梦境转折点,将所有左端点小于等于该转折点的区间加入一个堆。
- 堆按照右端点排序,每次取右端点最小的进行匹配。
- 显然这样进行贪心不会使答案更差。
- 时间复杂度$Theta(NlogN)$,空间复杂度$Theta(N)$。
#include<cstdio> #include<algorithm> #include<queue> using namespace std; int const N=2e5+5,lar=1e9+7; int n,m; struct node{ int l,r; friend bool operator < (node skyh,node yxs){ return skyh.r>yxs.r; } }s[N]; int a[N],ans; priority_queue<node>q; bool vis[N]; inline int read(){ int ss(0);char bb(getchar()); while(bb<48||bb>57)bb=getchar(); while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar(); return ss; } int main(){ //freopen("1.in","r",stdin); n=read(),m=read(); for(register int i=1;i<=n;++i)s[i].l=read(),s[i].r=read(); for(register int i=1;i<=m;++i)a[i]=read(); sort(s+1,s+n+1,[](node skyh,node yxs){ return skyh.l<yxs.l; }); sort(a+1,a+m+1); int now=1,la=1; while(now<=n||la<=m){ while(now<=n && s[now].l<=a[la])q.push(s[now++]); while(!q.empty() && q.top().r<a[la])q.pop(); if(!q.empty())q.pop(),++ans; ++la; } printf("%d",ans); return 0; }
T2
- 概率期望,考试继续坚持dfs。
- 觉得正解不是很好想到,可能是自己太弱了吧。
- 还是复述一边题解?
- 设dp[i][j]表示i个点组成的森林有j个点在第一棵树的概率。
- 有$dp[i][j]=dp[i-1][j-1]*(j-1)*inv[i]+dp[i-1][j]*(i-j)*inv[i]$。
- 因为新的点既可以成为i-1个点的父节点又可以独立成一棵树所以是inv[i]。
- 再设f[i][j]表示i个点组成的树深度不超过j的概率,g[i][j]表示i个点组成的森林深度不超过j的概率。
- $f[i][j]=g[i-1][j-1]$。因为深度不超过j点数为i的树去掉根节点就变成了深度不超过j-1点数为i-1的森林,而且不同的树对应不同的森林。
- $g[i][j]=sumlimits_{k=1}^idp[i][k]*f[k][j]*g[i-k][j]$。
- 表示深度不超过j点数为i的森林由一棵点数为k深度不超过j的树和一个点数为i-k深度不超过j的森林转移。
- 因为考虑了所有情况,所以事实上森林中的树是有序的,所以没有重复。
- 注意对于$forall jin[i,n]$有$f[i][j]=g[i][j]=1$。
- 然而我并不能通过样例,没脸的我又颓了另一篇题解,被告知长度=深度-1的神奇之事。改了改答案统计就AC了……
- 时间复杂度$Theta(N^3)$,空间复杂度$Theta(N^2)$。
#include<cstdio> #define ll long long using namespace std; inline int read(){ int ss(0);char bb(getchar()); while(bb<48||bb>57)bb=getchar(); while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar(); return ss; } const int N=202,n=read(),p=read(); ll dp[N][N],f[N][N],g[N][N],inv[N]; inline ll power(ll x,int y){ ll ans=1; for(;y;y>>=1,x=x*x%p) if(y&1)ans=ans*x%p; return ans; } inline int max(int x,int y){ return x>y?x:y; } int main(){ //freopen("1.in","r",stdin); dp[1][1]=1; inv[0]=inv[1]=1; for(register int i=2;i<=n;++i)inv[i]=power(i,p-2); for(register int i=2;i<=n;++i) for(register int j=1;j<=i;++j) dp[i][j]=(dp[i-1][j-1]*(j-1)+dp[i-1][j]*(i-j))%p*inv[i]%p; for(register int i=0;i<=n;++i)g[0][i]=1; for(register int i=1;i<=n;++i){ for(register int j=1;j<i;++j)f[i][j]=g[i-1][j-1]; for(register int j=i;j<=n;++j)f[i][j]=1; for(register int j=0;j<i;++j){ for(register int k=1;k<=i;++k) g[i][j]=(g[i][j]+f[k][j]*g[i-k][j]%p*dp[i][k]%p)%p;//,printf("%d %d %lld ",i,k,dp[i][k]); } for(register int j=i;j<=n;++j)g[i][j]=1; } ll ans=0; for(register int i=2;i<=n;++i)//{ //printf("%d %lld ",i,f[n][i]); ans=(ans+(f[n][i]-f[n][i-1]+p)*(i-1))%p; //} printf("%lld",ans); return 0; }
T3
- 考虑莫队。
- 先说说考试时我如何用线段树把$Theta(1)$的转移优化成$Theta(logN)$吧。
- 因为每次转移只会增加或减少一个节点,所以只需知道该节点的父节点和单代子节点有多少没有受到暴风雪影响就可以转移。
- 首先记录每个点的状态,这样父节点可以直接查询。
- 对于子节点,我们用一个son数组表示没有受到暴风雪影响的单代子节点数量,每次修改一个节点时更新父节点的son值即可,复杂度$Theta(1)$。
- 然而蒟蒻的我并没有发现son可以直接维护,于是……
- 我维护了整棵树的bfs序,因为bfs序保证了任意一个节点单代子节点的位置连续。
- 然后用线段树维护权值就行,复杂度$Theta(logN)$,直接T成暴力。
- 莫队的转移说完了基本就没什么了,注意本题莫队需要先加点后删点,不然无法保证答案的正确性。
- 时间复杂度$Theta(Nsqrt N)$,空间复杂度$Theta(N)$。
- 关于块长,考试的时候调了半个小时,$n^{0.536}$应该比$sqrt n$优秀。
#include<cstdio> #include<algorithm> #include<cmath> #include<vector> #define L tr[k].lc #define R tr[k].rc using namespace std; int const N=2e5+5; int n,qs; int head[N],to[N<<1],Next[N<<1],t; struct node{ int l,r,id; }q[N]; int bl[N],fa[N]; int son[N]; bool vis[N]; int ans[N],as; inline int read(){ int ss(0);char bb(getchar()); while(bb<48||bb>57)bb=getchar(); while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar(); return ss; } inline void _swap(int &x,int &y){ int z=x; x=y,y=z; } void dfs(int x,int y){ fa[x]=y; for(int i=head[x];i;i=Next[i]) if(to[i]^y)dfs(to[i],x); return ; } inline void add(int x,int y){ to[++t]=y; Next[t]=head[x],head[x]=t; return ; } int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); n=read(),qs=read(); for(register int i=1,ff,tt;i<n;++i) ff=read(),tt=read(),add(ff,tt),add(tt,ff); for(register int i=1;i<=qs;++i)q[i].l=read(),q[i].r=read(),q[i].id=i; dfs(1,0); int blk=pow(n,0.536),ct=n/blk,ln=0,rn=0; for(register int i=1;i<=ct;++i){ ln=rn+1,rn+=blk; for(register int j=ln;j<=rn;++j) bl[j]=i; } if(rn<n){ ++ct; for(register int i=rn+1;i<=n;++i)bl[i]=ct; } sort(q+1,q+qs+1,[](node skyh,node yxs){ return (bl[skyh.l]^bl[yxs.l])?skyh.l<yxs.l:((bl[skyh.l]&1)?skyh.r<yxs.r:skyh.r>yxs.r); }); ln=q[1].l,rn=q[1].r; for(register int i=ln,k;i<=rn;++i){ vis[i]=1; int tot=vis[fa[i]]+son[i]; as-=tot-1,++son[fa[i]]; } ans[q[1].id]=as; for(register int i=2,lg,rg,k,lp=ln,rp=rn;i<=qs;++i){ lg=q[i].l,rg=q[i].r; while(lp>lg){ vis[--lp]=1; int tot=vis[fa[lp]]+son[lp]; as-=tot-1,++son[fa[lp]]; } while(rp<rg){ vis[++rp]=1; int tot=vis[fa[rp]]+son[rp]; as-=tot-1,++son[fa[rp]]; } while(lp<lg){ vis[lp]=0; int tot=vis[fa[lp]]+son[lp]; as+=tot-1,--son[fa[lp++]]; } while(rp>rg){ vis[rp]=0; int tot=vis[fa[rp]]+son[rp]; as+=tot-1,--son[fa[rp--]]; } ans[q[i].id]=as; } for(register int i=1;i<=qs;++i)printf("%d ",ans[i]); return 0; }