A.梦境
如果不用去重一定要用Multiset……挂30分算是出题人手下留情了。
贪心。把点排序,区间按右端点递增排序。依次考虑每个区间,取能选的最靠左的点即可。multiset维护。
#include<cstdio> #include<iostream> #include<cstring> #include<set> #include<algorithm> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } const int N=2e5+5; int n,m,t[N],vis[N],ans; struct inv { int l,r; friend bool operator < (const inv &x,const inv &y) { return x.r<y.r; } }a[N]; multiset<int> s; multiset<int>::iterator it1,it2; int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i].l=read(),a[i].r=read(); for(int i=1;i<=m;i++) t[i]=read(); sort(t+1,t+m+1); for(int i=1;i<=m;i++) s.insert(t[i]); sort(a+1,a+n+1); for(int i=1;i<=n;i++) { if(s.empty())break; it1=s.lower_bound(a[i].l); it2=s.upper_bound(a[i].r); if(it1==s.end())continue; if(it2!=s.begin())it2--; else continue; if((*it1)>(*it2))continue; ans++; s.erase(it1); } cout<<ans<<endl; return 0; }
B.玩具
注意问题的转化方式以及辅助数组的正确使用姿势。
比较难的计数往往需要探究已给出条件的一些性质,并把抽象的转移过程具体化。本题的$f[i][j]=g[i-1][j-1]$就是一个很好的例子。
还有就是状态的正确设定,计数一般不会有特别鬼畜的转移方式,所以状态数组的含义十分重要。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=205; ll n,mod; ll dp[N][N],g[N][N],f[N][N],inv[N]; // dp:in the 1st tree // f:tree's depth <=j // g:forest's depth <=j ll qpow(ll a,ll b) { ll res=1;a=a%mod; while(b) { if(b&1)res=res*a%mod; a=a*a%mod; b>>=1; } return res; } int main() { scanf("%lld%lld",&n,&mod); dp[1][1]=f[1][0]=1; for(int i=1;i<=n;i++) inv[i]=qpow(i,mod-2); for(int i=2;i<=n;i++) for(int j=1;j<=i;j++) dp[i][j]=(dp[i-1][j-1]*1LL*(j-1)%mod*inv[i]%mod+dp[i-1][j]*1LL*(i-j)%mod*inv[i]%mod)%mod; for(int i=0;i<=n;i++) g[0][i]=1; for(int i=1;i<=n;i++) for(int j=0;j<=n;j++) { if(j)f[i][j]=g[i-1][j-1]; for(int k=1;k<=i;k++) (g[i][j]+=f[k][j]*g[i-k][j]%mod*dp[i][k]%mod)%=mod; } ll ans=0; for(int i=1;i<=n;i++) (ans+=(f[n][i]-f[n][i-1]+mod)%mod*i%mod)%=mod; cout<<ans<<endl; return 0; }
C.飘雪圣域
森林的联通块个数=点数-边数,记住了!!
上次考场能YY出来这次却想不到QAQ……
那么对于每次询问,点数已知,只需要求出这些点之间有多少边即可。
设一条边连接了$x$和$y$。那么把边排序,以$x$为下标把$y$上传到主席树上,每次查询区间有多少数 $leq y$即可。
连图都不用建2333
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } const int N=2e5+5; int n,Q; struct edge { int x,y; friend bool operator < (const edge &a,const edge &b) { return (a.x==b.x)?(a.y<b.y):(a.x<b.x); } }e[N<<1]; int root[N<<5],ls[N<<5],rs[N<<5],size[N<<5],pos[N],type; void update(int &k,int l,int r,int old,int val) { k=++type; ls[k]=ls[old];rs[k]=rs[old]; size[k]=size[old]+1; if(l==r)return ; int mid=l+r>>1; if(val<=mid)update(ls[k],l,mid,ls[old],val); else update(rs[k],mid+1,r,rs[old],val); } int query(int k,int l,int r,int val) { if(l==r)return size[k]; int mid=l+r>>1; if(val<=mid)return query(ls[k],l,mid,val); else return size[ls[k]]+query(rs[k],mid+1,r,val); } int main() { n=read();Q=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[i]=(edge){min(x,y),max(x,y)}; } sort(e+1,e+n); for(int i=1;i<n;i++) { update(root[i],1,n,root[i-1],e[i].y); if(!pos[e[i].x])pos[e[i].x]=i; } pos[n]=n;root[n]=root[n-1]; for(int i=n;i;i--) if(!pos[i])pos[i]=pos[i+1]; while(Q--) { int l=read(),r=read(); int lc=pos[l],rc=pos[r]; int num=query(root[rc],1,n,r)-query(root[lc-1],1,n,r); printf("%d ",r-l+1-num); } return 0; }