20190818考试反思
题不难,可我答的不够好。
T1时间太长,忘了公式又推了半天。边码对拍边试公式,大概开考40分钟才A掉。
T2想的太少,还是思路差一点拔高,大众分都能想到,应该能顺着想出来线段树思路。
T3。。。。。因为给的时间过少,set建边不会,摸索半天,大概30分钟才把边建出来,然后打的时候发现我的dfs思路死了就剩10min了,立马想到他tarjan+topu,然后10分钟码完了撞了一个变量名,爆0了。
这次考试大概比较简单,但是问题有
1.线段树的运用不够灵活,一般是能够化出来一个式子,把和变量有关的放到一块,扔到树上,然后用常量进行判断修改查询等。
2.T3有向图无向图搞混,这是不应该的,在确定要用图论来做的时候,先判断用哪部分知识。
T1:裸catalan,没了。
#include<iostream> #include<cstdio> using namespace std; const int N=2000020,mod=20100403; int fac[N],inv[N]; inline int rd() { int s=0,w=1; char cc=getchar(); for(;cc<'0'||cc>'9';cc=getchar()) if(cc=='-') w=-1; for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0'; return s*w; } int qpow(int a,int k) { int ans=1; for(;k;k>>=1,a=1ll*a*a%mod) if(k&1) ans=1ll*ans*a%mod; return ans; } int C(int n,int m) { if(n<m) return 0; return 1ll*fac[n]*inv[n-m]%mod*inv[m]%mod; } int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); int n=rd(),m=rd();fac[0]=1; if(n<m) return puts("0"),0; for(int i=1;i<=n+m;i++) fac[i]=1ll*fac[i-1]*i%mod; inv[n+m]=qpow(fac[n+m],mod-2); for(int i=n+m;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod; printf("%d\n",(C(n+m,n)-C(n+m,m-1)+mod)%mod); } /* g++ 1.cpp -o 1 ./1 4 3 */
T2:线段树思路很顺利,不知为什么他们都沉迷于排序二分。暴力$O(nm)$很好打,考虑要优化哪部分,因为$O(m)$的部分每次都不一样,所以不优化它,考虑优化每一轮的计算,我们要的是一段能喝的权值和,只要排除不能喝的贡献即可,咋就不能喝了,$c[i]<ans+i$就不行了,就是当前的答案和这一轮他之前和他自己的消耗的次数,变量放一边$c[i]-i>=ans$为合法,只要区间最小值,然后如果都合法,那么直接加整个区间的贡献,否则对点挨个删,删到合法后加贡献,注意删完一个区间后边的排序就少一$c[i]-i$应该加一,然后就没了。每个点最多删一次,复杂度$O(nlogn)$
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=100020; long long inf=0x7ffffffffffffff; long long f[N],a[N],c[N],w[N]; struct tree{long long w,c,f,p;}tr[N*4]; inline long long rd() { long long s=0,w=1; register char cc=getchar(); for(;cc<'0'||cc>'9';cc=getchar()) if(cc=='-') w=-1; for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0'; return s*w; } inline void updata(int k) { tr[k].w=tr[k<<1].w+tr[k<<1|1].w; tr[k].c=min(tr[k<<1].c,tr[k<<1|1].c); if(tr[k<<1].c<=tr[k<<1|1].c) tr[k].p=tr[k<<1].p; else tr[k].p=tr[k<<1|1].p; } inline void modify(int k,int l,int r,int w){tr[k].c+=w;tr[k].f+=w;} inline void down(int k,int l,int r) { int mid=l+r>>1; modify(k<<1,l,mid,tr[k].f); modify(k<<1|1,mid+1,r,tr[k].f); tr[k].f=0; } void build(int k,int l,int r) { if(l==r) { tr[k].c=c[l]-l; tr[k].w=1; tr[k].p=l; return; } int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); updata(k); } void cut(int k,int l,int r,int id) { if(l==r){tr[k].w=0,tr[k].c=inf;return;} int mid=l+r>>1; if(tr[k].f)down(k,l,r); if(id<=mid) cut(k<<1,l,mid,id); else cut(k<<1|1,mid+1,r,id); updata(k); } void add(int k,int l,int r,int x,int y,int w) { if(l==x&&r==y){modify(k,l,r,w);return;} int mid=l+r>>1; if(tr[k].f) down(k,l,r); if(y<=mid) add(k<<1,l,mid,x,y,w); else if(x>mid) add(k<<1|1,mid+1,r,x,y,w); else add(k<<1,l,mid,x,mid,w),add(k<<1|1,mid+1,r,mid+1,y,w); updata(k); } int main() { int n=rd(),m=rd(); long long x=rd(); for(int i=1;i<=n;i++) w[i]=rd(); for(int i=1;i<=n;i++) a[i]=rd(); for(int i=1;i<=n;i++) { c[i]=(x-w[i])/a[i]+1; if(c[i]<0) c[i]=0; } build(1,1,n); long long ans=0; while(m--) { while(tr[1].c<ans) { int t=tr[1].p; cut(1,1,n,t); add(1,1,n,t,n,1); } ans+=tr[1].w; } printf("%lld\n",ans); } /* g++ -std=c++11 2.cpp -o 2 ./2 5 2 20 15 14 13 12 12 1 1 1 1 1 */
T3:sb题,正解$O(n^{2})$,我服了,最终还是看出来topu了,也打出来了,但是太紧张导致撞变量了。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=100020; long long inf=0x7ffffffffffffff; long long f[N],a[N],c[N],w[N]; struct tree{long long w,c,f,p;}tr[N*4]; inline long long rd() { long long s=0,w=1; register char cc=getchar(); for(;cc<'0'||cc>'9';cc=getchar()) if(cc=='-') w=-1; for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0'; return s*w; } inline void updata(int k) { tr[k].w=tr[k<<1].w+tr[k<<1|1].w; tr[k].c=min(tr[k<<1].c,tr[k<<1|1].c); if(tr[k<<1].c<=tr[k<<1|1].c) tr[k].p=tr[k<<1].p; else tr[k].p=tr[k<<1|1].p; } inline void modify(int k,int l,int r,int w){tr[k].c+=w;tr[k].f+=w;} inline void down(int k,int l,int r) { int mid=l+r>>1; modify(k<<1,l,mid,tr[k].f); modify(k<<1|1,mid+1,r,tr[k].f); tr[k].f=0; } void build(int k,int l,int r) { if(l==r) { tr[k].c=c[l]-l; tr[k].w=1; tr[k].p=l; return; } int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); updata(k); } void cut(int k,int l,int r,int id) { if(l==r){tr[k].w=0,tr[k].c=inf;return;} int mid=l+r>>1; if(tr[k].f)down(k,l,r); if(id<=mid) cut(k<<1,l,mid,id); else cut(k<<1|1,mid+1,r,id); updata(k); } void add(int k,int l,int r,int x,int y,int w) { if(l==x&&r==y){modify(k,l,r,w);return;} int mid=l+r>>1; if(tr[k].f) down(k,l,r); if(y<=mid) add(k<<1,l,mid,x,y,w); else if(x>mid) add(k<<1|1,mid+1,r,x,y,w); else add(k<<1,l,mid,x,mid,w),add(k<<1|1,mid+1,r,mid+1,y,w); updata(k); } int main() { int n=rd(),m=rd(); long long x=rd(); for(int i=1;i<=n;i++) w[i]=rd(); for(int i=1;i<=n;i++) a[i]=rd(); for(int i=1;i<=n;i++) { c[i]=(x-w[i])/a[i]+1; if(c[i]<0) c[i]=0; } build(1,1,n); long long ans=0; while(m--) { while(tr[1].c<ans) { int t=tr[1].p; cut(1,1,n,t); add(1,1,n,t,n,1); } ans+=tr[1].w; } printf("%lld\n",ans); } /* g++ -std=c++11 2.cpp -o 2 ./2 5 2 20 15 14 13 12 12 1 1 1 1 1 */
Zeit und Raum trennen dich und mich.时空将你我分开。