NOIP2017赛前模拟11月6日—7日总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2017赛前模拟11月6日—7日总结相关的知识,希望对你有一定的参考价值。
收获颇丰的两天···
题目1:序列操作
给定n个非负整数,进行m次操作,每次操作给出c,要求找出c个正整数数并将它们减去1,问最多能进行多少操作?n,m<=1000000
首先暴力贪心肯定是每次减去数中前c大的数··
因此我们考虑每次减去前c大的数后依然保持数列的有序性,假设数列为111223,c=5,为了保持有序性,2和3的部分可以正常减去1,但1的话我们需要从最左边开始减···
所以对应每次操作,我们需要找到减去的最小的数的区间··从最左边开始减···这样就能保持有序性,直接在线段树上维护区间最大与区间最小值,二分即可
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<algorithm> #include<cctype> using namespace std; const int N=1e6+5; int n,m,h[N],tag[N*4],trmi[N*4],trmx[N*4],le,ri; inline int R(){ char c;int f=0,i=1; for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) i=-1,c=getchar(); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f*i; } inline bool cmp(int a,int b){return a>b;} inline void build(int k,int l,int r){ if(l==r){ trmx[k]=trmi[k]=h[l];return; } int mid=(l+r)>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); trmi[k]=min(trmi[k<<1],trmi[k<<1|1]); trmx[k]=max(trmx[k<<1],trmx[k<<1|1]); } inline void pushdown(int k){ if(tag[k]){ trmi[k<<1]-=tag[k];trmi[k<<1|1]-=tag[k];trmx[k<<1]-=tag[k];trmx[k<<1|1]-=tag[k]; tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k];tag[k]=0; } } inline int query(int k,int l,int r,int x){ if(l==r) return trmi[k]; int mid=(l+r)>>1;pushdown(k); if(x<=mid) return query(k<<1,l,mid,x); else return query(k<<1|1,mid+1,r,x); } inline void getle(int k,int l,int r,int x){ if(l==r){ le=l;return; } int mid=(l+r)>>1;pushdown(k); if(trmi[k<<1]>x) getle(k<<1|1,mid+1,r,x); else getle(k<<1,l,mid,x); } inline void getri(int k,int l,int r,int x){ if(l==r){ ri=l;return; } int mid=(l+r)>>1;pushdown(k); if(trmx[k<<1|1]<x) getri(k<<1,l,mid,x); else getri(k<<1|1,mid+1,r,x); } inline void modify(int k,int l,int r,int x,int y){ if(l>=x&&r<=y){ tag[k]++;trmi[k]--;trmx[k]--;return; } int mid=(l+r)>>1;pushdown(k); if(x<=mid) modify(k<<1,l,mid,x,y); if(y>mid) modify(k<<1|1,mid+1,r,x,y); trmi[k]=min(trmi[k<<1],trmi[k<<1|1]); trmx[k]=max(trmx[k<<1],trmx[k<<1|1]); } int main(){ //freopen("a.in","r",stdin); //freopen("mine.out","w",stdout); n=R(),m=R();int x;int ans=0; for(int i=1;i<=n;i++) h[i]=R(); sort(h+1,h+n+1,cmp);build(1,1,n); while(m--){ x=R(); int now=query(1,1,n,x); if(!now) break;ans++; getle(1,1,n,now);getri(1,1,n,now); if(le==1) modify(1,1,n,ri-x+1,ri); else modify(1,1,n,1,le-1),modify(1,1,n,ri+le-x,ri); } cout<<ans<<endl; return 0; }
题目2:图:
题目题解见:http://blog.csdn.net/white_elephant/article/details/78418239?locationNum=5&fps=1
本来删边加边是可以用LCT维护的···然而考虑到时NOIP题这里暴力跳点维护也是可以过的··于是我就尝试写了一发暴力··调到怀疑人生··主要是跳点的时候细节有点多···
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; const int M=2e5+5; const int inf=0x3f3f3f3f; struct node{int x,y,val;}A[M],B[M],C[M]; struct node1{double bsli;int pre,now;}ans[M]; struct node2{int x,id;}query[M]; int father[N],deep[N],dis[N],fa[N],n,q,a,b,cnt,from[N],visit[N],tim; int first[N],next[N*2],go[N*2],tot,val[N*2]; long long Anss[M]; inline int R(){ char c;int f=0,i=1; for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) i=-1,c=getchar(); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f*i; } inline int getfa(int a){ if(father[a]==a) return a; else return father[a]=getfa(father[a]); } inline void comb(int a,int b,int c){ next[++tot]=first[a],first[a]=tot,go[tot]=b,val[tot]=c; next[++tot]=first[b],first[b]=tot,go[tot]=a,val[tot]=c; } inline void dfs(int u,int Fa){ for(int e=first[u];e;e=next[e]){ int v=go[e];if(v==Fa) continue; deep[v]=deep[u]+1;fa[v]=u;dis[v]=val[e];dfs(v,u); } } inline bool cmp(const node &a,const node &b){ return a.val<b.val; } inline void modify(int a,int b,int now){ visit[a]=++tim;int tmp=a; while(tmp!=1) visit[fa[tmp]]=tim,tmp=fa[tmp]; int to=b;while(visit[to]!=tim) to=fa[to]; int maxx=-inf,pos,op,tempa=a,tempb=b; if(fa[a]==to){ if(dis[a]>maxx) maxx=dis[a],pos=a,op=1; } else if(a!=to){ while(fa[a]!=to){ if(dis[a]>maxx){ maxx=dis[a];pos=a;op=1; } from[fa[a]]=a;a=fa[a]; } if(dis[a]>maxx) maxx=dis[a],pos=a,op=1; } if(fa[b]==to){ if(dis[b]>maxx) maxx=dis[b],pos=b,op=2; } else if(b!=to){ while(fa[b]!=to){ if(dis[b]>maxx){ maxx=dis[b];pos=b;op=2; } from[fa[b]]=b;b=fa[b]; } if(dis[b]>maxx) maxx=dis[b],pos=b,op=2; } ans[++cnt].pre=maxx;ans[cnt].now=B[now].val; ans[cnt].bsli=(double)(B[now].val-maxx)/2; if(op==2){ while(pos!=tempb){ dis[pos]=dis[from[pos]],fa[pos]=from[pos];pos=from[pos]; } dis[tempb]=-2*inf;fa[tempb]=tempa; } else{ while(pos!=tempa){ dis[pos]=dis[from[pos]],fa[pos]=from[pos];pos=from[pos]; } dis[tempa]=-2*inf;fa[tempa]=tempb; } } inline void getans(){ sort(B+1,B+b+1,cmp); for(int i=1;i<=b;i++){ modify(B[i].x,B[i].y,i); } } inline bool cmp2(node1 a,node1 b){ return a.bsli<b.bsli; } inline bool cmp3(node2 a,node2 b){ return a.x<b.x; } int main(){ //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); n=R(),a=R(),b=R(),q=R(); for(int i=1;i<=a;i++) A[i].x=R(),A[i].y=R(),A[i].val=R(); for(int i=1;i<=b;i++) B[i].x=R(),B[i].y=R(),B[i].val=R(); for(int i=1;i<=n;i++) father[i]=i; int temp=0;ans[cnt=1].bsli=-(2e+9); long long sum=0; sort(A+1,A+a+1,cmp); for(int i=1;i<=a;i++){ int fx=getfa(A[i].x),fy=getfa(A[i].y); if(fx!=fy){ sum+=A[i].val;temp++;father[fx]=fy; comb(A[i].x,A[i].y,A[i].val); } if(temp==n-1) break; } deep[1]=0;dis[1]=0;fa[1]=0;dfs(1,0); temp=0;for(int i=1;i<=n;i++) father[i]=i; sort(B+1,B+b+1,cmp); for(int i=1;i<=b;i++){ int fx=getfa(B[i].x),fy=getfa(B[i].y); if(fx!=fy){ temp++;C[temp]=B[i];father[fx]=fy; } if(temp==n-1) break; } b=temp;for(int i=1;i<=b;i++) B[i]=C[i]; getans();sort(ans+1,ans+cnt+1,cmp2); int tail=1; for(int i=1;i<=q;i++) query[i].x=R(),query[i].id=i; sort(query+1,query+q+1,cmp3); tail=1; for(int i=1;i<=q;i++){ double v=query[i].x*1.0; while(tail<cnt&&v>=ans[tail+1].bsli) tail++,sum+=ans[tail].now-ans[tail].pre; Anss[query[i].id]=sum+(long long)(n-1-2*(tail-1))*v; } for(int i=1;i<=q;i++) cout<<Anss[i]<<endl; return 0; }
题目3:路径统计
已知一个有向图,有n个节点,每个节点只有一条n出边,边有权值,求两两节点之间最短路之和%1e^7,若两节点没有连通则最短路记为-1,n<=500000
先说题解:我们可以发现整个图就是一个森林,且每部分就是一颗奇环内向树··因此我们先处理环上对答案的贡献,再处理与环直接相连的点,最后再树形dp即可(只能口胡了··详细说起来有点麻烦··看代码吧)···
但是这道题真的是恶心到极点啊··首先统计答案的时候要时时刻刻记得取模,不然很容易错,想象一下考试时自己辛辛苦苦调出来对拍也没错但交上去全tmwa仅仅因为少tm取了几个模的情景··而且很容易爆int,以后遇到这种题能全开long long就全开long long······
但题的基本除去去模部分还是一道挺好的题··
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> #include<queue> #include<vector> using namespace std; const int mod=1e9+7; const int N=6e5+5; priority_queue< pair<long long,int> >que; vector<int>cir[N]; long long fst[N],nxt[N*2],go[N*2],val[N*2],tot=0,n; long long fst2[N],nxt2[N*2],go2[N*2],tot2=0,val2[N*2],dfn[N],low[N],Id[N],stk[N],top=0,totg,from[N],dp[N]; long long dis[N],ans=0,C[N],sum[N],f[N],cnt=0,size[N]; bool vst[N],insta[N]; inline int R(){ char c;int f=0,i=1; for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) i=-1,c=getchar(); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f*i; } inline void comb(int a,int b,int c){ nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b,val[tot]=c; } inline void comb1(int a,int b,int c){ nxt2[++tot2]=fst2[a],fst2[a]=tot2,go2[tot2]=b,val2[tot2]=c; } inline void tarjan(int u){ dfn[u]=low[u]=++cnt;stk[++top]=u,insta[u]=true; for(int e=fst[u];e;e=nxt[e]){ int v=go[e]; if(!dfn[v]){ tarjan(v);low[u]=min(low[v],low[u]); } else if(insta[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ totg++; while(stk[top]!=u){ cir[totg].push_back(stk[top]),Id[stk[top]]=totg,size[totg]++,insta[stk[top]]=false,top--; } cir[totg].push_back(stk[top]),Id[stk[top]]=totg,size[totg]++,insta[stk[top]]=false,top--; } } inline void dfs(int u,int now){ vst[u]=true; for(int e=fst[u];e;e=nxt[e]){ int v=go[e];if(Id[u]!=Id[v]) continue; if(!vst[v]) C[now]=(C[now]+val[e])%mod,from[v]=val[e],dfs(v,now); else{ C[now]=(C[now]+val[e])%mod,from[v]=val[e];return; } } } inline void dfs2(int u){ vst[u]=true; for(int e=fst[u];e;e=nxt[e]){ int v=go[e]; if(vst[v]) dp[u]+=dp[v]+1,f[u]=(f[u]+(f[v]+val[e]*(dp[v]+1)%mod)%mod)%mod; else{ dfs2(v);dp[u]+=dp[v]+1,f[u]=(f[u]+(f[v]+val[e]*(dp[v]+1)%mod)%mod)%mod; } } cnt=(cnt+dp[u])%mod,ans=(ans+f[u])%mod; } inline void calc(int u){ long long temp1=0;temp1=(temp1+from[cir[u][0]])%mod,vst[cir[u][0]]=true; for(int i=1;i<size[u];i++) temp1=(temp1+from[cir[u][i]]*(size[u]-i+1)%mod)%mod,vst[cir[u][i]]=true; sum[cir[u][0]]=temp1-C[u]; for(int i=1;i<size[u];i++) temp1=(temp1+C[u])%mod,temp1=((temp1-size[u]*from[cir[u][i]])%mod+mod)%mod,sum[cir[u][i]]=((temp1-C[u])%mod+mod)%mod; } int main(){ //freopen("road.in","r",stdin); //freopen("road.out","w",stdout); int _q=50<<20; char *_p=(char*)malloc(_q)+_q; __asm__("movl %0, %%esp\n"::"r"(_p)); n=R();int a,b; for(int i=1;i<=n;i++){ a=R(),b=R();if(a==i) continue; comb(i,a,b);comb1(a,i,b); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=totg;i++) reverse(cir[i].begin(),cir[i].end()); cnt=0; for(int i=1;i<=totg;i++){ if(size[i]<2) continue; dfs(cir[i][0],i); cnt=(cnt+size[i]*(size[i]-1)%mod)%mod; ans=(ans+(size[i]*(size[i]-1)/2)%mod*C[i]%mod)%mod; calc(i); } for(int i=1;i<=totg;i++){ if(size[i]==1) continue; for(int j=0;j<size[i];j++){ int u=cir[i][j]; for(int e=fst2[u];e;e=nxt2[e]){ int v=go2[e];if(Id[v]==Id[u]) continue; f[v]=(sum[u]+val2[e]*size[i]%mod)%mod;vst[v]=true;dp[v]=size[i]; ans=(ans+f[v])%mod;cnt=(cnt+size[i])%mod; } } } for(int i=1;i<=n;i++) if(!vst[i]) dfs2(i); long long temp=((long long)n*(n-1)-cnt)%mod;temp=(temp%mod+mod)%mod; cout<<((ans-temp)%mod+mod)%mod<<endl; return 0; }
题目4:小店购物
有n件商品,每件商品有自己的价值w和价格p,每次购买时会有k元,每次会购买价值最高(如果价值相等则选价格最低)且价格低于所剩的钱的商品,直到不能买为止(每件商品可以购买无数次)
有p次操作,两种类型:一是询问如果有k元能买价值总和多大的商品,二是将某个商品的价格价值更改,n<=10000,m<=100000
考虑暴力操作,我们按题意找到符合题意的商品,价格为pri,价值为val,所剩钱为k,那么本次购买对答案的贡献就是k/pri*val,且k变为k%pri,每次购买计算贡献次数不会超过logk次
很明显我们可以用线段树维护,区间为离散化后的商品价值,区间储存商品价格的最小值···每次查找时贪心先找右子树即可,然而一个商品价值可能对应多个价格,因此在线段树的叶子节点我们需要用一个multiset来维护一下节点中的元素
注意multiset在删除操作时如果是删除键值会将该键值的迭代器全部删完,所以要先找到迭代器再删除元素
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> #include<set> using namespace std; const int N=2e5+5; const int inf=0x3f3f3f3f; multiset<int>::iterator t; multiset<int>st[N*8]; struct node{int op,x,w,p;}q[N]; inline int R(){ char c;int f=0; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f; } int w[N],p[N],b[N*2],cnt,n,m,tree[N*8],pri,val,pos[N]; inline void lsh(){ sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-b-1; for(int i=1;i<=n;i++) w[i]=lower_bound(b+1,b+cnt+1,w[i])-b; for(int i=1;i<=m;i++) if(q[i].op==1) q[i].w=lower_bound(b+1,b+cnt+1,q[i].w)-b; } inline void insert(int k,int l,int r,int x,int y,int now){ if(l==r){ p[now]=y;pos[now]=k;st[k].insert(y);tree[k]=min(tree[k],y);return; } int mid=(l+r)/2; if(x<=mid) insert(k*2,l,mid,x,y,now); else insert(k*2+1,mid+1,r,x,y,now); tree[k]=min(tree[k*2],tree[k*2+1]); } inline void find(int k,int l,int r,int x){ if(l==r){ pri=tree[k];val=b[l];return; } int mid=(l+r)/2; if(tree[k]>x) return; if(tree[k*2+1]>x) find(k*2,l,mid,x); else find(k*2+1,mid+1,r,x); } inline void del(int k,int x,int y){ if(k==x){ st[k].erase(st[k].find(y)); if(st[k].empty()) tree[k]=inf; else{ t=st[k].begin();tree[k]=*t; } del(k/2,x,y);return; } tree[k]=min(tree[k*2],tree[k*2+1]); if(k==1) return; else del(k/2,x,y); } int main(){ //freopen("shopping.in","r",stdin); //freopen("shopping.out","w",stdout); n=R(),m=R(); for(int i=1;i<=n;i++) w[i]=R(),p[i]=R(),b[++cnt]=w[i]; for(int i=1;i<=m;i++){ q[i].op=R(); if(q[i].op==1){ q[i].x=R();q[i].w=R();q[i].p=R();b[++cnt]=q[i].w; } else q[i].x=R(); } lsh();memset(tree,inf,sizeof(tree)); for(int i=1;i<=n;i++) insert(1,1,cnt,w[i],p[i],i); for(int i=1;i<=m;i++){ if(q[i].op==2){ int temp=q[i].x;long long ans=0; while(true){ pri=val=0;find(1,1,cnt,temp); if(pri==0&&val==0) break; else ans+=(long long)temp/pri*val,temp%=pri; } cout<<ans<<endl; } else{ del(pos[q[i].x],pos[q[i].x],p[q[i].x]); insert(1,1,cnt,q[i].w,q[i].p,q[i].x);w[q[i].x]=q[i].w; } } return 0; }
以上是关于NOIP2017赛前模拟11月6日—7日总结的主要内容,如果未能解决你的问题,请参考以下文章