CSP-S模拟题(补几天的坑,62~69)
Posted heoitys
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSP-S模拟题(补几天的坑,62~69)相关的知识,希望对你有一定的参考价值。
模拟62
很显然的一个性质是旅行次数为一个联通块中边数/2向下取整,树DP+贪心走一边DFS即可求出方案
#include<bits/stdc++.h> using namespace std; typedef pair<int,int> P; typedef pair<P,int> D; struct edge{ int u,v; inline int get(int x){return x==u?v:u;} }e[200050]; int N,M,last=0; bool vis[100050],used[200050]; vector<int>to[100050]; vector<D>ans; void dfs(int u,int pre){ vis[u]=1; for(int i=0,y,v;i<to[u].size();++i){ y=to[u][i];v=e[y].get(u); if(vis[v])continue;dfs(v,y); } for(int i=0,y,v;i<to[u].size();++i){ y=to[u][i];v=e[y].get(u); if(used[y]||y==last||y==pre)continue; if(last)ans.push_back(D(P(e[last].get(u),v),u)),used[last]=1,used[y]=1,last=0;else last=y; } if(last&&pre){ ans.push_back(D(P(e[last].get(u),e[pre].get(u)),u)),used[last]=1,used[pre]=1,last=0; } } int main(){ cin>>N>>M; for(int i=1,u,v;i<=M;++i)scanf("%d%d",&u,&v),to[u].push_back(i),to[v].push_back(i),e[i].u=u,e[i].v=v; for(int i=1;i<=N;++i){ last=0; if(!vis[i])dfs(i,0); } cout<<ans.size()<<endl; for(int i=0;i<ans.size();++i)cout<<ans[i].first.first<<" "<<ans[i].second<<" "<<ans[i].first.second<<endl; } /* 4 5 1 2 3 2 2 4 3 4 4 1 */
Permutation (好题)
正串最小,反串最大(不会证明),解法是建边跑拓扑排序
#include<iostream> #include<cstdio> #include<queue> using namespace std; int n,k,p[510000],q[510000],to[1010000],nex[1010000],head[510000],indu[510000],tot; int minn[2010000]; struct node{ int x; bool operator < (const node b)const{ return x>b.x; } }; priority_queue<node>qwq; void add(int x,int y){ to[++tot]=y,nex[tot]=head[x],head[x]=tot; indu[y]++; } void add_point(int t,int l,int r,int x,int k){ if(l==r){ minn[t]=k; return; } int mid=(l+r)>>1; if(mid>=x) add_point(t*2,l,mid,x,k); else add_point(t*2+1,mid+1,r,x,k); minn[t]=min(minn[t*2],minn[t*2+1]); } int query(int t,int l,int r,int L,int R){ if(L>R) return n+1; if(L<=l&&r<=R){ return minn[t]; } int mid=(l+r)>>1,ans=n+1; if(mid>=L) ans=min(ans,query(t*2,l,mid,L,R)); if(mid<R) ans=min(ans,query(t*2+1,mid+1,r,L,R)); return ans; } void build(int t,int l,int r){ minn[t]=n+1; if(l==r) return; int mid=(l+r)>>1; build(t*2,l,mid); build(t*2+1,mid+1,r); } int main(){ scanf("%d%d",&n,&k); for(register int i=1;i<=n;i++) scanf("%d",&p[i]),q[p[i]]=i; build(1,1,n); for(register int i=n;i>=1;i--){ int x=query(1,1,n,max(1,q[i]-k+1),q[i]),y=query(1,1,n,q[i],min(n,q[i]+k-1)); if(x<=n) add(q[i],q[x]); if(y<=n) add(q[i],q[y]); add_point(1,1,n,q[i],i); } for(register int i=1;i<=n;i++) if(indu[i]==0) qwq.push((node){i}); int tm=0; while(qwq.size()){ int x=qwq.top().x;qwq.pop(); q[++tm]=x; for(register int i=head[x];i;i=nex[i]){ int y=to[i]; indu[y]--; if(indu[y]==0) qwq.push((node){y}); } } for(register int i=1;i<=n;i++) p[q[i]]=i; for(register int i=1;i<=n;i++) printf("%d ",p[i]); }
考虑让经过最小边的路径最少使得答案最大,那么可以发现是能够只走一次最小边,向下划分子问题,最后答案就是~边权和(-_- !)
#include<iostream> #include<cstdio> using namespace std; int n; long long ans; int main(){ //freopen("3.in","r",stdin); //freopen("3.out","w",stdout); scanf("%d",&n); for(register int i=1,x,y,z;i<n;i++){ scanf("%d%d%d",&x,&y,&z); ans+=z; } printf("%lld ",ans); }
模拟 63
Median (好题)
离散化,数据可以看成是随机的,然后开桶记录数出现的次数,移动窗口的同时移动中位数,只是千万别sort(我不知道,我没改完,我被停 训了~因为没值日)
#include<bits/stdc++.h> using namespace std; int n,k,w; long long tmp[11000000],S[11000000],prm[11000000],l,r,num1,num2; char vis[110000000]; void move_right(){num1++,num2--,l++;while(tmp[l]==0&&l<=n) l++;} void move_left(){num1--,num2++,l--;while(tmp[l]==0&&l) l--;} struct node{ int x,id,dis; }p[11000000]; char cmp1(node a,node b){ return a.x<b.x; } char cmp2(node a,node b){ return a.id<b.id; } int main(){ //freopen("1.in","r",stdin); //freopen("2.out","w",stdout); scanf("%d%d%d",&n,&k,&w); for(register int i=2;i<=100000000;i++){ if(!vis[i]) prm[++prm[0]]=i; for(register int j=1;j<=prm[0]&&i*prm[j]<=100000000;j++){ vis[i*prm[j]]=1; if(i%prm[j]==0) break; } if(prm[0]==n) break; } for(register int i=1;i<=n;i++){ S[i]=prm[i]*i%w; p[i].x=S[i]+S[(int)floor(1.0*i/10.0)+1]; p[i].id=i; } sort(p+1,p+n+1,cmp1); for(register int i=1;i<=n;i++){ p[i].dis=i,S[i]=p[i].x; } sort(p+1,p+n+1,cmp2); double ans=0; if(k&1){ l=p[1].dis; tmp[p[1].dis]++; for(register int i=2;i<=k;i++){ tmp[p[i].dis]++; if(p[i].dis>l) num2++; else num1++; } while(num1<num2) move_right(); while(num2<num1) move_left(); ans+=S[l]; for(register int i=k+1;i<=n;i++){ tmp[p[i].dis]++; if(p[i].dis>l) num2++; else num1++; tmp[p[i-k].dis]--; if(p[i-k].dis==l){ if(num2) move_right(); else move_left(); } if(p[i-k].dis>l) num2--; else num1--; while(num1<num2) move_right(); while(num2<num1) move_left(); ans+=S[l]; } } else{ l=p[1].dis; tmp[p[1].dis]++; for(register int i=2;i<=k;i++){ tmp[p[i].dis]++; if(p[i].dis>l) num2++; else num1++; } while(num1<num2-1) move_right(); while(num2<num1+1) move_left(); r=l+1; while(tmp[r]==0&&r<=n) r++; ans=(1.0*(S[l]+S[r]))/2.0; // cout<<(1.0*S[l]+1.0*S[r])/2.0<<endl; for(register int i=k+1;i<=n;i++){ tmp[p[i].dis]++; if(p[i].dis>l) num2++; else num1++; tmp[p[i-k].dis]--; if(p[i-k].dis==l){ if(num2) move_right(); else move_left(); } if(p[i-k].dis>l) num2--; else num1--; while(num1<num2-1) move_right(); while(num2<num1+1) move_left(); r=l+1; while(tmp[r]==0&&r<=n) r++; ans+=((1.0*(S[l]+S[r]))/2.0); //cout<<(1.0*S[l]+1.0*S[r])/2.0<<endl; } } printf("%.1lf ",ans); }
乍一看还以为博弈论,实际就是每个人都拿集合中最大的数,而集合中的最大值不升,如果要进集合的数大于集合最大值,拿走这个数,否则 拿走最大值并把这个数加进集合
不要用堆维护最大值,因为单调不升,开桶~
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int n,k,tmp[110000],a[110000],b[110000]; long long sum[2]; int main(){ scanf("%d%d",&n,&k); for(register int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]; b[0]=n; sort(b+1,b+b[0]+1); b[0]=unique(b+1,b+b[0]+1)-b-1; for(register int i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b; } int p; while(k--){ scanf("%d",&p); int l=0,cur=0; for(register int i=1;i<=p;i++) tmp[a[i]]++,l=l>a[i]?l:a[i]; sum[0]=sum[1]=0; sum[0]=b[l]; tmp[l]--; while(tmp[l]==0&&l) l--; for(register int i=p+1;i<=n;i++){ cur^=1; if(a[i]>=l) sum[cur]+=b[a[i]]; else{ sum[cur]+=b[l]; tmp[l]--; tmp[a[i]]++; while(tmp[l]==0&&l) l--; } } while(l){ cur^=1; sum[cur]+=b[l]; tmp[l]--; while(tmp[l]==0&&l) l--; } printf("%lld ",sum[0]-sum[1]); } }
树上DP,维护两个三位DP数组,一个表示从子树中走到根,一个表示从根走向子树,撒了多少面包屑,该点撒还是不撒,合并是考虑周全
#include<iostream> #include<cstdio> using namespace std; int n,v,to[210000],nex[210000],head[110000],tot; long long f[110000][110][2],tmp[110000][110][2],sum[110000],c[110000],ans; void add(int x,int y){ to[++tot]=y,nex[tot]=head[x],head[x]=tot; } void dfs(int x,int pre){ sum[x]=c[pre]; for(register int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre) continue; dfs(y,x); sum[x]+=c[y]; } } void DFS(int x,int pre){ f[x][1][1]=sum[x]; tmp[x][1][1]=sum[x]; for(register int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre) continue; DFS(y,x); for(register int j=0;j<=v;j++){ ans=max(ans,max(tmp[x][j][0]+max(f[y][v-j][0],f[y][v-j][1]),tmp[x][j][1]+max(f[y][v-j][0],f[y][v-j][1])-c[y])); ans=max(ans,max(f[x][j][0]+max(tmp[y][v-j][0],tmp[y][v-j][1]),f[x][j][1]+max(tmp[y][v-j][0],tmp[y][v-j][1]))); } for(register int j=0;j<=v;j++){ if(j>0){ f[x][j][0]=max(f[x][j][0],f[x][j-1][0]); f[x][j][1]=max(f[x][j][1],f[x][j-1][1]); tmp[x][j][0]=max(tmp[x][j][0],tmp[x][j-1][0]); tmp[x][j][1]=max(tmp[x][j][1],tmp[x][j-1][1]); } f[x][j][0]=max(f[x][j][0],max(f[y][j][0],f[y][j][1])); if(j>0) f[x][j][1]=max(f[x][j][1],max(f[y][j-1][0],f[y][j-1][1])+sum[x]-c[y]); tmp[x][j][0]=max(tmp[x][j][0],max(tmp[y][j][0],tmp[y][j][1])); if(j>0) tmp[x][j][1]=max(tmp[x][j][1],max(tmp[y][j-1][1],tmp[y][j-1][0])+sum[x]); } } for(register int i=0;i<=v;i++){ ans=max(ans,max(f[x][i][1],f[x][i][0])); ans=max(ans,max(tmp[x][i][0],tmp[x][i][1])); tmp[x][i][1]-=c[pre]; //printf("x=%d i=%d f[%d][%d][1]=%lld f[%d][%d][0]=%lld tmp[%d][%d][1]=%lld tmp[%d][%d][0]=%lld ",x,i,x,i,f[x][i][1],x,i,f[x][i][0],x,i,tmp[x][i][1],x,i,tmp[x][i][0]); } } int main(){ scanf("%d%d",&n,&v); for(register int i=1;i<=n;i++) scanf("%lld",&c[i]); for(register int i=1,x,y;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0); DFS(1,0); printf("%lld",ans); }
模拟64
trade(好题)
反悔贪心
#include<iostream> #include<cstdio> #include<queue> #include<cmath> using namespace std; int n; long long ans; priority_queue<int>q; int main(){ scanf("%d",&n); for(register int i=1,x;i<=n;i++){ scanf("%d",&x); if(q.empty()||abs(q.top())>x) q.push(-x); else{ ans+=x+q.top(); q.pop(); q.push(-x); q.push(-x); } } printf("%lld ",ans); }
莫队
//不要忘了大样例 #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; int id,Q,bl[110000],blc; const int mod=1e9+7; long long fac[110000],inv[110000],ans[110000],cet; struct node{ int n,m,id; bool operator < (const node b)const{ return bl[n]==bl[b.n]?(bl[m]<bl[b.m]):(bl[n]<bl[b.n]); } }q[110000]; long long qpow(long long a,long long b){ long long ans=1; while(b){ if(b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } return ans; } int main(){ //freopen("sum3.in","r",stdin); //freopen("2.out","w",stdout); scanf("%d%d",&id,&Q); blc=sqrt(100000); fac[0]=1; for(register int i=1;i<=100000;i++) fac[i]=fac[i-1]*i%mod,bl[i]=(i-1)/blc; inv[100000]=qpow(fac[100000],mod-2); inv[0]=1; for(register int i=99999;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod; for(register int i=1;i<=Q;i++) scanf("%d%d",&q[i].n,&q[i].m),q[i].id=i; sort(q+1,q+Q+1); int n=1,m=0; cet=1; for(register int i=1;i<=Q;i++){ while(n<q[i].n) cet=(cet*2%mod-fac[n]*inv[m]%mod*inv[n-m]%mod)%mod,n++; while(m<q[i].m) m++,cet=(cet+fac[n]*inv[m]%mod*inv[n-m]%mod)%mod; while(m>q[i].m) cet=(cet-fac[n]*inv[m]%mod*inv[n-m]%mod)%mod,m--; while(n>q[i].n) n--,cet=(cet+fac[n]*inv[m]%mod*inv[n-m]%mod)%mod*inv[2]%mod; ans[q[i].id]=(cet+mod)%mod; } for(register int i=1;i<=Q;i++) printf("%lld ",ans[i]); }
大模拟
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; struct Hang{ int l,r,id; bool operator < (const Hang b)const{ return (l<b.l)||(l==b.l&&r<b.r); } }; struct Lie{ int u,d,id; bool operator < (const Lie b)const{ return (u<b.u)||(u==b.u&&d<b.d); } }; struct node{ int l,r,u,d,id; bool operator < (const node b)const{ return (u<b.u)||(u==b.u&&l<b.l); } }w[110000]; vector<Hang>hang[110000]; vector<Lie>lie[110000]; int id,n,m,k,q,fa[110000]; long long sumh[110000],sum[110000],num[110000]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} void search_h(Hang Lockey,int i,int e){ int j=lower_bound(hang[e].begin(),hang[e].end(),Lockey)-hang[e].begin()-1; for(;j<hang[e].size();j++){ if(hang[e][j].r<w[i].l) continue; if(hang[e][j].l>w[i].r) break; int x=find(w[i].id),y=find(hang[e][j].id); if(x!=y){ num[w[i].u]--; fa[x]=y; } } } void search_l(Lie dog,int i,int e){ int j=lower_bound(lie[e].begin(),lie[e].end(),dog)-lie[e].begin()-1; for(;j<lie[e].size();j++){ if(lie[e][j].d<w[i].u) continue; if(lie[e][j].u>w[i].u) break; int x=find(w[i].id),y=find(lie[e][j].id); if(x!=y){ num[w[i].u]--; fa[x]=y; } } } int main(){ scanf("%d%d%d%d%d",&id,&n,&m,&k,&q); for(register int i=1,u,l,d,r;i<=k;i++){ scanf("%d%d%d%d",&w[i].u,&w[i].l,&w[i].d,&w[i].r);w[i].id=i,fa[i]=i; if(w[i].u==w[i].d){ sumh[w[i].u]+=w[i].r-w[i].l+1; hang[w[i].u].push_back((Hang){w[i].l,w[i].r,i}); lie[w[i].r].push_back((Lie){w[i].u,w[i].d,i}); } else{ sum[w[i].u]++,sum[w[i].d+1]--; lie[w[i].l].push_back((Lie){w[i].u,w[i].d,i}); hang[w[i].d].push_back((Hang){w[i].l,w[i].r,i}); } } sort(w+1,w+k+1); for(register int i=1;i<=max(n,m);i++){ hang[i].push_back((Hang){-5,-5,0}); lie[i].push_back((Lie){-5,-5,0}); sort(hang[i].begin(),hang[i].end()); sort(lie[i].begin(),lie[i].end()); } for(register int i=1;i<=n;i++) sum[i]+=sum[i-1]; for(register int i=1;i<=n;i++) sum[i]+=sumh[i]+sum[i-1]; int u,v,e; for(register int i=1;i<=k;i++){ if(w[i].u==w[i].d){ num[w[i].u]++; Hang Lockey=(Hang){w[i].l,w[i].r,0}; e=w[i].u-1,search_h(Lockey,i,e); Lie dog=(Lie){w[i].u,w[i].u,0}; e=w[i].l-1,search_l(dog,i,e); e=w[i].r+1,search_l(dog,i,e); } else{ num[w[i].u]++; Lie dog=(Lie){w[i].u,w[i].u,0}; e=w[i].l-1,search_l(dog,i,e); e=w[i].r+1,search_l(dog,i,e); Hang Lockey=(Hang){w[i].l,w[i].r,0}; e=w[i].u-1,search_h(Lockey,i,e); } } for(register int i=1;i<=n;i++) num[i]+=num[i-1]; while(q--){ scanf("%d%d",&u,&v); if(u==0) printf("%lld ",sum[v]); else printf("%lld ",num[v]); } }
模拟65
不太会,听他们说跟小凯的疑惑有共通的地方----赛瓦维斯特定理,可以看这个gay的博客
#include<iostream> #include<cstdio> using namespace std; int T; long long n,m; long long q; long long gcd(long long a,long long b){return b?gcd(b,a%b):a;} int main(){ scanf("%d",&T); while(T--){ scanf("%lld%lld%lld",&n,&m,&q); long long g=gcd(n,m); long long cet=q; if(n>m) swap(n,m); long long num=0; for(register long long i=0;i<n/g;i++){ if(q-1ll*i*m<0) break; num+=((q-i*m)/n)+1; } printf("%lld ",cet-num+1); } }
拆边,将一个边拆成边权的因子的多条边,按因子可以将边分成几个集合,然后建树跑DFS求直径,也可以用按秩合并并查集+树剖求LCA+结 构体封装优化+卡常轻(艰)松(难)切掉
#include<iostream> #include<cstdio> #include<vector> using namespace std; int n,prm[410000],num[110],v[1100000],ans[410000],to[810000],nex[810000],head[410000],tot; struct BCJ{ int root,l,r,d,dep,tim; void clear(int x){ root=x,l=x,r=x,d=0,dep=1; } }bcj[410000]; struct curtree{ int dis,fa,son,size,top; }tr[410000]; void add(int x,int y){ to[++tot]=y,nex[tot]=head[x],head[x]=tot; } struct node{ int f,t,w; }e[410000]; vector<int>vec[1100000]; void search(int d,int ans,int id){ if(d==prm[0]+1){ vec[ans].push_back(id); return; } search(d+1,ans,id); for(register int i=1;i<=num[d];i++) ans*=prm[d],search(d+1,ans,id); } void fenjie(int x,int id){ prm[0]=0; while(x!=1){ prm[++prm[0]]=v[x]; int w=v[x]; num[prm[0]]=0; while(v[x]==w) x/=v[x],num[prm[0]]++; } search(1,1,id); } void DFS(int x,int pre){ tr[x].top=bcj[x].root=bcj[x].l=bcj[x].r=x; bcj[x].d=bcj[x].dep=0; tr[x].size=1; tr[x].fa=pre; tr[x].dis=tr[pre].dis+1; for(register int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre) continue; DFS(y,x); tr[x].son=tr[tr[x].son].size>tr[y].size?tr[x].son:y; tr[x].size+=tr[y].size; } } void dfs(int x,int pre){ if(tr[x].son) tr[tr[x].son].top=tr[x].top,dfs(tr[x].son,x); for(register int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre||y==tr[x].son) continue; dfs(y,x); } } int lca(int x,int y){ register int xx=tr[x].top,yy=tr[y].top; while(xx!=yy){ if(tr[xx].dis>tr[yy].dis) x=tr[xx].fa,xx=tr[x].top; else y=tr[yy].fa,yy=tr[y].top; } return tr[x].dis<tr[y].dis?x:y; } void merge(int x,int y){ register int u[5]; u[1]=bcj[x].l,u[2]=bcj[x].r,u[3]=bcj[y].l,u[4]=bcj[y].r; if(bcj[x].dep>bcj[y].dep) bcj[x].dep=max(bcj[x].dep,bcj[y].dep+1),bcj[y].root=bcj[x].root; else bcj[y].dep=max(bcj[x].dep+1,bcj[y].dep),bcj[x].root=bcj[y].root; for(register int i=1;i<=2;i++){ for(register int j=3;j<=4;j++){ int len=tr[u[i]].dis+tr[u[j]].dis-2*tr[lca(u[i],u[j])].dis; if(len>bcj[bcj[x].root].d){ bcj[bcj[x].root].l=u[i],bcj[bcj[x].root].r=u[j]; bcj[bcj[x].root].d=len; } } } } int find(register int x){ while(x!=bcj[x].root) x=bcj[x].root; return x; } void work(int x){ for(register int i=0;i<vec[x].size();i++){ register int u=vec[x][i]; if(bcj[e[u].f].tim!=x) bcj[e[u].f].clear(e[u].f); if(bcj[e[u].t].tim!=x) bcj[e[u].t].clear(e[u].t); register int fr=find(e[u].f),tt=find(e[u].t); merge(fr,tt); bcj[fr].tim=bcj[tt].tim=x; ans[bcj[bcj[fr].root].d]=max(ans[bcj[bcj[fr].root].d],x); } } inline int read(){ register int ret; register char r; while(r=getchar(),r<‘0‘||r>‘9‘); ret=r^48; while(r=getchar(),r>=‘0‘&&r<=‘9‘) ret=(ret<<1)+(ret<<3)+(r^48); return ret; } int main(){ // freopen("ex_walk2.in","r",stdin); // freopen("1.out","w",stdout); n=read(); int maxn=0; for(register int i=1;i<n;i++){ e[i].f=read(),e[i].t=read(),e[i].w=read(); add(e[i].f,e[i].t); add(e[i].t,e[i].f); maxn=max(maxn,e[i].w); } for(register int i=2;i<=maxn;i++){ if(!v[i]) v[i]=i,prm[++prm[0]]=i; for(register int j=1;j<=prm[0]&&i*prm[j]<=maxn;j++){ v[prm[j]*i]=prm[j]; if(i%prm[j]==0) break; } } for(register int i=1;i<n;i++) fenjie(e[i].w,i); DFS(1,0); dfs(1,0); for(register int i=1;i<=maxn;i++) work(i); for(register int i=n;i>=1;i--) ans[i]=max(ans[i],ans[i+1]); for(register int i=1;i<=n;i++) printf("%d ",ans[i]); }
#include<iostream> #include<cstdio> #include<vector> using namespace std; int n,prm[410000],num[110],v[1100000],ans[410000],to[24100000],nex[24100000],head[410000],tot,vis[410000]; void add(int x,int y){ to[++tot]=y,nex[tot]=head[x],head[x]=tot; } struct node{ int f,t,w; }e[410000]; vector<int>vec[1100000]; void search(int d,int ans,int id){ if(d==prm[0]+1){ vec[ans].push_back(id); return; } search(d+1,ans,id); for(register int i=1;i<=num[d];i++) ans*=prm[d],search(d+1,ans,id); } void fenjie(int x,int id){ prm[0]=0; while(x!=1){ prm[++prm[0]]=v[x]; int w=v[x]; num[prm[0]]=0; while(v[x]==w) x/=v[x],num[prm[0]]++; } search(1,1,id); } int d; int dfs(int x,int pre){ int maxn=0; vis[x]=1; for(register int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre) continue; int w=dfs(y,x); d=max(maxn+w,d); maxn=max(maxn,w); } return maxn+1; } void work(int x){ d=0; for(register int i=0;i<vec[x].size();i++){ int y=vec[x][i]; add(e[y].f,e[y].t); add(e[y].t,e[y].f); } for(register int i=0;i<vec[x].size();i++){ int y=vec[x][i]; if(!vis[e[y].f]) dfs(e[y].f,0); } ans[d]=x; for(register int i=0;i<vec[x].size();i++){ int y=vec[x][i]; head[e[y].f]=head[e[y].t]=0; vis[e[y].f]=vis[e[y].t]=0; } tot=0; } int main(){ scanf("%d",&n); for(register int i=2;i<=1000000;i++){ if(!v[i]) v[i]=i,prm[++prm[0]]=i; for(register int j=1;j<=prm[0]&&i*prm[j]<=1000000;j++){ v[prm[j]*i]=prm[j]; if(i%prm[j]==0) break; } } for(register int i=1;i<n;i++){ scanf("%d%d%d",&e[i].f,&e[i].t,&e[i].w); fenjie(e[i].w,i); } for(register int i=1;i<=1000000;i++) work(i); for(register int i=n;i>=1;i--) ans[i]=max(ans[i],ans[i+1]); for(register int i=1;i<=n;i++) printf("%d ",ans[i]); }
Travel(未填之坑)
贪心+模拟
模拟 66
经推(打)导(表) $f[i]=f[i-1]*i+(-1)^{[i&1]}$ 没用高精他挂了 ,正解式子
#include<iostream> #include<cstdio> using namespace std; int n,f[510]; char a[210]; void multi(int x){ int tmp=0; for(register int i=1;i<=f[0];i++){ tmp=f[i]*x+tmp; f[i]=tmp%10; tmp/=10; } while(tmp) f[++f[0]]=tmp%10,tmp/=10; } void jian(int x){ f[1]--; for(register int i=1;i<=f[0];i++){ if(f[i]<0) f[i]+=10,f[i+1]--; else break; } while(f[f[0]]==0) f[0]--; } void jia(int x){ f[1]++; for(register int i=1;i<=f[0];i++){ if(f[i]==10) f[i]=0,f[i+1]++; else break; } f[0]++; while(f[f[0]]==0) f[0]--; } int main(){ scanf("%d",&n); for(register int i=1;i<=n;i++) scanf("%s",a); f[++f[0]]=1; for(register int i=3;i<=n;i++){ multi(i); if(i&1) jian(1); else jia(1); } while(f[0]) cout<<f[f[0]],f[0]--; }
bitset 暴力水过,正解将q分正反向与p建图,判断是否是DAG
#include<iostream> #include<cstdio> #include<bitset> using namespace std; int T,n; char a[2100]; bitset<2020>P[2100],Q[2100]; int judge(){ for(register int i=1;i<=n;i++) for(register int j=1;j<=n;j++) if(P[i][j]==1) if((P[i]&P[j])!=P[j]) return 0; for(register int i=1;i<=n;i++) for(register int j=1;j<=n;j++) if(Q[i][j]==1) if((Q[i]&Q[j])!=Q[j]) return 0; return 1; } int main(){ //freopen("1.in","r",stdin); scanf("%d",&T); while(T--){ scanf("%d",&n); for(register int i=1;i<=n;i++){ scanf("%s",a+1); P[i].reset(),Q[i].reset(); for(register int j=1;j<=n;j++){ if(a[j]==‘P‘) P[i][j]=1; else if(a[j]==‘Q‘) Q[i][j]=1; } } if(judge()) puts("T"); else puts("N"); } }
异或(神题)(未填)
比数位DP还恶心的数位DP
模拟67
因为$ a+b|ab,a=k_1*c,b=k_2*c $所以 $a+b=(k_1+k_2)*cRightarrow ab=k_1k_2c^2 Rightarrow(k_1+k_2)|k_1k_2c $
又因为 $ gcd(k_1,k_2)=1 Rightarrow gcd(k_1,k_1k_2)=1,gcd(k_2,k_1k_2)=1 $所以$(k1+k2)|c且gcd(k_1,k_2)=1$
因为$a+b<=n$所以$c(k_1+k_2)<=n$ ,因为$ c=x*(k_1+k_2)$,所以$x*(k_1+k_2)^2<=n$
因此枚举$k_1+k_2=S$的S,那么x的个数就是$n/(S^2)$,而(k_1,k_2)的组数就是$ varphi(S) $
所以只要求 $sumlimits_{s=2}^{sqrt(n)}varphi(s)*n/(s^2) $
#include<iostream> #include<cstdio> #include<cmath> using namespace std; long long n; int phi[11000000],prm[700000]; int main(){ scanf("%lld",&n); phi[1]=1; for(register int i=2;i<=10000000;i++){ if(!phi[i]) phi[i]=i-1,prm[++prm[0]]=i; for(register int j=1;j<=prm[0]&&i*prm[j]<=10000000;j++){ if(i%prm[j]==0){phi[i*prm[j]]=phi[i]*prm[j];break;} else phi[i*prm[j]]=phi[i]*(prm[j]-1); } } long long ans=0; for(register int i=2;i<=sqrt(n);i++){ ans=ans+1ll*phi[i]*(n/(1ll*i*i)); } printf("%lld ",ans); }
LIS问题,树状数组维护最大值,(注意参数要读全,我AC代码因为没读入type而Wa10)
#include<iostream> #include<cstdio> using namespace std; int n,a[110000]; const long long mod=123456789; struct node{ int maxn; long long num; }tr[110000]; int lowbit(int x){return x&(-x);} node ask(int x){ node ans=(node){0,1}; while(x){ if(tr[x].maxn==ans.maxn) (ans.num+=tr[x].num)%=mod; else if(tr[x].maxn>ans.maxn) ans=tr[x]; x-=lowbit(x); } return ans; } void add(int x,node w){ while(x<=100000){ if(tr[x].maxn==w.maxn) (tr[x].num+=w.num)%=mod; else if(w.maxn>tr[x].maxn)tr[x]=w; x+=lowbit(x); } } int main(){ int shabi; scanf("%d%d",&n,&shabi); for(register int i=1;i<=n;i++) scanf("%d",&a[i]); for(register int i=1;i<=n;i++){ node w=ask(a[i]-1); w.maxn++; add(a[i],w); } node ans=ask(100000); if(shabi) printf("%d %lld ",ans.maxn,ans.num); else printf("%d ",ans.maxn); }
幻魔皇 (好题)
懒得写了,还是那个gay的幻魔皇题解
#include<iostream> #include<cstdio> using namespace std; int n; long long sumW[5100],sumB[5100],f[5100],g[5100],ans[11000]; const int mod=123456789; int main(){ scanf("%d",&n); sumW[1]=sumB[2]=f[0]=1; for(register int i=3;i<=n;i++) sumW[i]=(sumW[i-1]+sumW[i-2])%mod, sumB[i]=(sumB[i-1]+sumB[i-2])%mod; for(register int i=1;i<=n;i++) sumW[i]=(sumW[i]+sumW[i-1])%mod, sumB[i]=(sumB[i]+sumB[i-1])%mod; for(register int i=1;i<=n;i++) g[i]=(g[i-1]+f[i-1])%mod,f[i]=g[i-1]; for(register int i=1;i<=n;i++) ans[i]=(ans[i]+f[i]*sumW[n-i]%mod)%mod; for(register int i=1;i<=n;i++) for(register int j=1;j<=n;j++) ans[i+j]=(ans[i+j]+f[i-1]*g[j-1]%mod*sumB[n-max(i,j)]%mod)%mod; for(register int i=1;i<=2*n;i++) printf("%lld ",ans[i]%mod); }
模拟68
肯定是删够m个才有最大的交集,枚举删i个a最小的,那么删去m-i个b最小的,a用sort排序,b放堆里面
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> using namespace std; int T,n,m,v[110000]; struct node{ int id,a,b; bool operator < (const node x)const{ return a<x.a; } }w[110000]; struct Node{ int id,a,b; bool operator < (const Node x)const{ return (b>x.b)||(b==x.b&&a>x.a); } }; priority_queue<Node>q; int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); for(register int i=1;i<=n;i++){ scanf("%d%d",&w[i].a,&w[i].b),w[i].id=i; } sort(w+1,w+n+1); long long ans=0,minna=w[m+1].a; for(register int i=m+1;i<=n;i++) q.push((Node){w[i].id,w[i].a,w[i].b}); for(register int i=m;i>=0;i--){ ans=max(ans,minna*q.top().b); if(w[i].b>=q.top().b) minna=min(minna,1ll*w[i].a),q.pop(),q.push((Node){w[i].id,w[i].a,w[i].b}); } while(q.size())q.pop(); printf("%lld ",ans); } }
e (好题)
树上主席树或主席树上树(^_^)维护一个点到根的那条链上的点权,所要查的联通块其实就是所有pi 到他们lca 的链的并
用pi的主席树减lca的父亲的主席树,查询r的前驱和后继
#include<iostream> #include<cstdio> #include<cmath> using namespace std; int n,q,type,a[110000],fa[110000][19],dis[110000]; int to[210000],nex[210000],head[110000],tot; int root[110000],ls[8100000],rs[8100000],num[8100000],cet; int p[110000]; const int upw=1e9; void add(int x,int y){ to[++tot]=y,nex[tot]=head[x],head[x]=tot; } void add_point(int &t1,int t2,int l,int r,int x){ if(!t1) t1=++cet; if(l==r){ num[t1]=num[t2]+1; return; } int mid=(l+r)>>1; if(mid>=x){ add_point(ls[t1],ls[t2],l,mid,x); rs[t1]=rs[t2]; } else{ add_point(rs[t1],rs[t2],mid+1,r,x); ls[t1]=ls[t2]; } num[t1]=num[ls[t1]]+num[rs[t1]]; } int ask_max(int t1,int t2,int l,int r,int L,int R){ if(num[t1]==num[t2]) return 0; if(l==r) return l; int mid=(l+r)>>1,ans=0; if(mid<R) ans=ask_max(rs[t1],rs[t2],mid+1,r,L,R); if(ans) return ans; if(mid>=L) ans=ask_max(ls[t1],ls[t2],l,mid,L,R); if(ans) return ans; return 0; } int ask_min(int t1,int t2,int l,int r,int L,int R){ if(num[t1]==num[t2]) return 0; if(l==r) return l; int mid=(l+r)>>1,ans=0; if(mid>=L) ans=ask_min(ls[t1],ls[t2],l,mid,L,R); if(ans) return ans; if(mid<R) ans=ask_min(rs[t1],rs[t2],mid+1,r,L,R); if(ans) return ans; return 0; } void dfs(int x,int pre){ fa[x][0]=pre; dis[x]=dis[pre]+1; add_point(root[x],root[pre],1,upw,a[x]); for(register int i=1;i<=18;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(register int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre) continue; dfs(y,x); } } int lca(int x,int y){ if(x==0) return y; if(dis[x]>dis[y]) swap(x,y); int i=0; for(;(1<<i)<=dis[y];i++); for(register int j=i;j>=0;j--) if(dis[fa[y][j]]>=dis[x]) y=fa[y][j]; if(x==y) return x; for(register int j=i;j>=0;j--) if(fa[y][j]!=fa[x][j]) y=fa[y][j], x=fa[x][j]; return fa[x][0]; } int main(){ scanf("%d%d%d",&n,&q,&type); for(register int i=1;i<=n;i++) scanf("%d",&a[i]); for(register int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0); long long r,k,las=0; while(q--){ scanf("%lld%lld",&r,&k); int lc=0; las%=n; for(register int i=1;i<=k;i++){ scanf("%d",&p[i]); p[i]=(p[i]-1+1ll*las*type%n)%n+1; lc=lca(lc,p[i]); } las=0x7ffffffffffff; for(register int i=1;i<=k;i++){ int w1=ask_max(root[p[i]],root[fa[lc][0]],1,upw,1,r); int w2=ask_min(root[p[i]],root[fa[lc][0]],1,upw,r,upw); if(w1&&w2) las=min(las,min(1ll*r-w1,1ll*w2-r)); else if(w2) las=min(las,1ll*w2-r); else las=min(las,1ll*r-w1); } printf("%lld ",las); } }
f(神题)(未填)
前i-1高位相同,第i为不同,那么这两个数的大小关系取决于res的第i位,即每一位i的贡献之间没有影响
01trie树求每一位是0或1的贡献,可以暴力算出每一个res的贡献排序,这样可以拿到55分
正解是二分f[res],求出有p-1个f值小于f[res],即满足f[res]是第p小
然后求出最小的res
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,k,p; int num[30000000],to[30000000][2],tot=1,t[31000000]; long long sum[35][2]; struct node{ int id; long long sum; bool operator < (const node b)const{ return (sum<b.sum)||(sum==b.sum&&id<b.id); } }w[50000000]; void add(int x){ int root=1,c[35]; for(register int i=0;i<k;i++,x>>=1) c[i]=x&1; for(register int i=k-1;i>=0;i--){ if(to[root][c[i]]==0) to[root][c[i]]=++tot; sum[i][c[i]]+=num[to[root][c[i]^1]]; root=to[root][c[i]]; num[root]++; } } int main(){ scanf("%d%d%d",&n,&k,&p); for(register int i=1,x;i<=n;i++){ scanf("%d",&x); add(x); } long long cet=0; for(register int i=0;i<k;i++){ t[1<<i]=i; sum[i][1]=sum[i][1]-sum[i][0],cet+=sum[i][0]; } w[0].id=0,w[0].sum=cet; for(register int i=1;i<(1<<k);i++){ w[i].sum=w[i^(i&(-i))].sum+sum[t[i&(-i)]][1]; w[i].id=i; } nth_element(w+0,w+p-1,w+(1<<k)); printf("%lld %d ",w[p-1].sum,w[p-1].id); }
模拟69
$O(n^4log)$DP,预处理转移系数,去掉log
#include<iostream> #include<cstdio> using namespace std; int n,num; long long f[110][11000],C[110][110],dp[110][11000]; long long fac[11000]; long long m; const int mod=1e9+7; long long qpow(long long a,long long b){ long long ans=1; while(b){ if(b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } return ans%mod; } int main(){ scanf("%d%lld%d",&n,&m,&num); if(m==n){ fac[0]=1; for(register int i=1;i<=n*n;i++) fac[i]=fac[i-1]*i%mod; printf("%lld ",fac[n*n]*qpow(fac[num]*fac[n*n-num]%mod,mod-2)%mod); return 0; } for(register int i=0;i<=n;i++){ C[i][0]=1; for(register int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; } if(num>n*n/2) num=n*n-num; for(register int i=1;i<=n;i++){ long long tim=m/n; if(m%n>=i) tim++; tim%=(mod-1); for(register int j=0;j<=num;j++){ dp[i][j]=qpow(C[n][j],tim); } } f[0][0]=1; for(register int i=1;i<=n;i++){ for(register int j=0;j<=num;j++){ for(register int k=0;k<=j;k++){ f[i][j]=(f[i][j]+f[i-1][k]*dp[i][j-k]%mod)%mod; } } } printf("%lld ",f[n][num]); }
array(好题)
单调栈维护单调不下降序列,如果当前走到i且弹完栈插入i,那么栈中元素k到i之间没有比a[k]更小的元素,所以只需要找k到i之间最大的 元素的位置s,来用s-k+1更新答案
可以用一个数组maxn[k]记录k到当前点之间最大的元素的位置,显然栈顶的maxn可以更新栈内的所有元素的maxn,(top出栈的时候,和下 一个取max传下去即可)
所以是$O(1)$更新,总复杂度$O(n)$
#include<iostream> #include<cstdio> using namespace std; int n,a[11000000],st[11000000],maxn[11000000]; inline int read(){ register int ret; register char r; while(r=getchar(),r<‘0‘||r>‘9‘); ret=r^48; while(r=getchar(),r>=‘0‘&&r<=‘9‘) ret=(ret<<1)+(ret<<3)+(r^48); return ret; } int main(){ //freopen("2.in","r",stdin); //freopen("2.out","w",stdout); n=read(); for(register int i=1;i<=n;i++) a[i]=read(); int ans=0; for(register int i=1;i<=n+1;i++){ while(st[0]&&a[st[st[0]]]>a[i]){ if(a[maxn[st[0]-1]]<=a[maxn[st[0]]]) maxn[st[0]-1]=maxn[st[0]]; ans=max(ans,maxn[st[0]]-st[st[0]]+1); maxn[st[0]]=0; st[0]--; } if(i==n+1) break; st[++st[0]]=i; maxn[st[0]]=i; } printf("%d ",ans); }
ants(好题)
莫队+线段树,但是会TLE50
可以用回滚莫队+链表思想
这里用到了一个更强大的莫队——回滚莫队:
如果待查区间在同一个块或在两个相邻的块,暴力扫,
如果待查区间在不同的块(块不相邻)里,将整块暴力扫进去,然后在这基础上向左向右扩展不到一个块的部分,统计完答案后delet恢 复原来的基础状态(整块的状态)
特殊的,我们把块编号[1,n],对于当前基础状态包含块[l,r-1],而当前整块有[l,r],那么在原状态的基础上加上r 这个块的贡献,并 把块[l,r]的贡献作为新的基础状态
非常优雅
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; int n,m,blc,bl[110000],a[110000],ans[110000]; int L[110000],R[110000],st[110000],top,upw,cet; struct node{ int l,r,id; char operator < (const node b)const{ return bl[l]==bl[b.l]?(r<b.r):(bl[l]<bl[b.l]); } }q[110000]; inline int read(){ register int ret; register char r; while(r=getchar(),r<‘0‘||r>‘9‘); ret=r^48; while(r=getchar(),r>=‘0‘&&r<=‘9‘) ret=(ret<<1)+(ret<<3)+(r^48); return ret; } void add(int x){ L[x]=R[x]=x; if(L[x-1]) L[x]=min(L[x],L[x-1]); if(R[x+1]) R[x]=max(R[x],R[x+1]); if(L[x-1]) R[L[x-1]]=R[x]; if(R[x+1]) L[R[x+1]]=L[x]; st[++top]=x; cet=max(cet,R[x]-L[x]+1); } void del(){ while(top>upw){ int x=st[top--]; if(L[x]==x&&R[x]==x) L[x]=R[x]=0; else if(L[x]==x){ L[R[x]]=x+1; R[x+1]=R[x]; L[x]=R[x]=0; } else if(R[x]==x){ R[L[x]]=x-1; L[x-1]=L[x]; L[x]=R[x]=0; } else{ L[R[x]]=x+1; R[L[x]]=x-1; L[x-1]=L[x]; R[x+1]=R[x]; L[x]=R[x]=0; } } } int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); n=read(),m=read(); blc=sqrt(n); for(register int i=1;i<=n;i++) a[i]=read(),bl[i]=(i-1)/blc+1; for(register int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i; sort(q+1,q+m+1); int l=0,r=0; for(register int i=1;i<=m;i++){ if(bl[q[i].r]-bl[q[i].l]<=1){ cet=upw=0; del(); for(register int j=q[i].l;j<=q[i].r;j++) add(a[j]); ans[q[i].id]=cet; del(); l=r=0; } else{ if(l!=bl[q[i].l]*blc){ upw=cet=0; del(); r=l=bl[q[i].l]*blc,add(a[l]); } while(r<(bl[q[i].r]-1)*blc) ++r,add(a[r]); upw=top; int las=cet; while(l>q[i].l) --l,add(a[l]); while(r<q[i].r) ++r,add(a[r]); ans[q[i].id]=cet; cet=las; del(); l=bl[q[i].l]*blc,r=(bl[q[i].r]-1)*blc; } } for(register int i=1;i<=m;i++) printf("%d ",ans[i]); }
总结:
好几天没写博客,一下子全整完真的挺累的。
不过发现其实时不时的回头看看做过的题也挺好,收获也蛮多的。
还有30多天就要CSP-S了,希望我能多进步一点儿,但愿能留下来吧。
以上是关于CSP-S模拟题(补几天的坑,62~69)的主要内容,如果未能解决你的问题,请参考以下文章