【20170521校内模拟赛】热爱生活的小Z
2017-05-22 09:19
183 查看
学长FallDream所出的模拟赛,个人感觉题目难度还是比较适中的,难度在提高+左右,可能比较接近弱省省选,总体来讲试题考查范围较广,个人认为还是很不错的。
所有试题如无特殊声明,开启-O2优化,时限1s,内存上限为128MB
总复杂度为\(O(n) / O(n)\)
总复杂度为$O(n^{3}/32) / O(n^2) $
有2种情况会使得整个朋友圈的不满意度+1
1.一个人选择了自己不想做的事
2.一对朋友做了不同的事
求最小不满意度。
具体建图方式如下:
1)喜欢划水的,向源点连一条容量为1的边;
2)喜欢学习的,向汇点连一条容量为1的边;
3)在朋友关系间,互连一条容量为1的边;
对上图求最小割即为本体正解。
总复杂度为$O(Maxflow(n,n^2)) / O(n^2) $
求\(\Sigma_{i=1}^{n} f(i,k)\) 有多组询问。
接下来对于每次询问,统计所有在\(max(n)\)范围内的所有满足当前\(k\)限制的贡献后计算答案,显然使用线段树是可以较为方便的实现这一操作的。
考虑对\([1,n]\)开一棵线段树统计当前的\(f(i,k)\)然后维护即可。
根据调和级数,我们有\[1+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n} \approx \ln {n}\]
故时间复杂度为\(O(n\ln n \log_{2} n +m\log_{2}nm)\)空间复杂度为\(O(m+n)\).
所有试题如无特殊声明,开启-O2优化,时限1s,内存上限为128MB
T1(seq)小Z爱序列
题意简析
给出一个1~n的全排列,问有多少对\((i,j)\)满足\(a_{i}<max( a_{[i+1,j-1]} ) < a_{j}\)数据范围及约定
\(1<=n<=10^6\)解题思路
通过观察,我们可以发现,答案在从右向左更新时,在某处的贡献为其右侧大于其的数的数量,故考虑使用单调栈维护右侧数单调性,每次先入队再计算贡献即可。总复杂度为\(O(n) / O(n)\)
#include <stdio.h> #define MN 1000005 #define r register #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++) char BB[1<<15],*S=BB,*TT=BB; inline int in(){ r int x; r bool f; r char c; for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-'); for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0'); return f?-x:x; } int n,ma,x,pos=1,a[MN],t,que[MN]; long long ans; int main(){ n=in();for (r int i=1; i<=n; ++i) a[i]=in(); for (r int i=n; i; --i){ while (a[i]>que[t]&&t) --t; que[++t]=a[i];if (t>2) ans+=t-2ll; }printf("%lld",ans); }
T2(graph)小Z爱图论
题意简析
给定一张有向图,求有多少点对\((i,j)\)满足从\(i\)是可达\(j\)的.数据范围及约定
\(1<=n<=2*10^3,1<=i,j<=n\)解题思路
考虑使用floyd解决连通性问题,发现可以通过bitset优化效率,故可以通过此题.总复杂度为$O(n^{3}/32) / O(n^2) $
#include <stdio.h> #include <bitset> #define MN 2005 #define r register #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),S==TT)?EOF:*S++) char BB[1<<15],*S=BB,*TT=BB; using std::bitset; inline int read(){ r int x; r bool f; r char c; for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-'); for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0'); return f?-x:x; } bitset<MN> b[MN];int n,ans; int main(){ n=read();for (r int i=1; i<=n; ++i) for (r int j=1; j<=n; ++j) b[i][j]=(read()|(i==j)); for (r int k=1; k<=n; ++k) for (r int i=1; i<=n; ++i) if (b[i][k]) b[i]|=b[k]; for (r int i=1; i<=n; ++i) ans+=b[i].count(); printf("%d",ans); }
T3(boat)小Z爱划水
题意简析
有\(n\)个人,每个人可以选择划水或学习,有\(m\)对朋友关系,每个人有自己想做的事。有2种情况会使得整个朋友圈的不满意度+1
1.一个人选择了自己不想做的事
2.一对朋友做了不同的事
求最小不满意度。
数据范围及约定
\(1<=n<=3*10^2,1<=m<=n*(n+1)/2\)解题思路
容易发现,这是一个经典的网络流模型,对于选择了划水的人,我们将其割至源点,对于选择了学习的人,我们将其割至汇点,显然题意就是求最小割。具体建图方式如下:
1)喜欢划水的,向源点连一条容量为1的边;
2)喜欢学习的,向汇点连一条容量为1的边;
3)在朋友关系间,互连一条容量为1的边;
对上图求最小割即为本体正解。
总复杂度为$O(Maxflow(n,n^2)) / O(n^2) $
#include <stdio.h> #include <string.h> #define r register #define S 0 #define T 301 #define MN 305 #define ME 100005 #define inf 0x3f3f3f3f #define min(a,b) ((a)<(b)?(a):(b)) #define getchar() (SS==TT&&(TT=(SS=BB)+fread(BB,1,1<<15,stdin),TT==SS)?EOF:*SS++) char BB[1<<15],*SS=BB,*TT=BB; inline int in(){ r int x; r bool f; r char c; for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-'); for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0'); return f?-x:x; } struct node{int to,nxt,v;}edge[ME]; int head[MN],lev[MN],n,m,cnt=1,que[MN],iter[MN]; inline void ins(int x,int y,int f){edge[++cnt]=(node){y,head[x],f};head[x]=cnt;} inline void insw(int x,int y,int f){ins(x,y,f);ins(y,x,0);} inline bool bfs(){ memset(lev,-1,sizeof(lev)); r int h=0,t=1;que[1]=S;lev[S]=0; while(h<t){ r int u=que[++h]; for (r int i=head[u]; i; i=edge[i].nxt) if (lev[edge[i].to]==-1&&edge[i].v){ r int v=edge[i].to;lev[v]=lev[u]+1; que[++t]=v; } } memcpy(iter,head,sizeof(head)); return lev[T]!=-1; } int dfs(int u,int v,int f){ if (u==v) return f; r int used=0; for (r int &i=iter[u]; i; i=edge[i].nxt) if (lev[edge[i].to]==lev[u]+1&&edge[i].v){ r int w=dfs(edge[i].to,v,min(f-used,edge[i].v)); used+=w;edge[i].v-=w;edge[i^1].v+=w; if (used==f) return f; } if (!used) lev[u]=-1;return used; } int dinic(){ r int flow=0; while(bfs()){ r int nf=dfs(S,T,inf); while(nf) flow+=nf,nf=dfs(S,T,inf); }return flow; } void init(){ n=in(),m=in();for (r int i=1; i<=n; ++i) in()?insw(S,i,1):insw(i,T,1); for (r int i=1; i<=m; ++i){r int x=in(),y=in();ins(x,y,1);ins(y,x,1);} } int main(){init(); printf("%d",dinic()); return 0;}
T4(math)小Z爱数学
题意简析
定义函数\(f(n,k)\)表示\(n\)的所有因数\(p_{i}\)中满足\(n/p_{i}<=k\)的\(p_{i}\)的和。求\(\Sigma_{i=1}^{n} f(i,k)\) 有多组询问。
数据范围及约定
\(询问数1<=m<=5*10^5,1<=n,k<=10^5\)解题思路
显然,如果不考虑\(k\)的限制,对于一个正整数\(x\),\(x\)的贡献为全体\(x\)的倍数,并且我们容易发现\(f(n,k)\)在\(n\)一定时,随\(k\)单调递增,故考虑对询问按\(k\)排序。接下来对于每次询问,统计所有在\(max(n)\)范围内的所有满足当前\(k\)限制的贡献后计算答案,显然使用线段树是可以较为方便的实现这一操作的。
考虑对\([1,n]\)开一棵线段树统计当前的\(f(i,k)\)然后维护即可。
根据调和级数,我们有\[1+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n} \approx \ln {n}\]
故时间复杂度为\(O(n\ln n \log_{2} n +m\log_{2}nm)\)空间复杂度为\(O(m+n)\).
#include <stdio.h> #include <algorithm> #define r register #define ll long long #define MN 500005 #define M (1<<17) #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++) char BB[1<<15],*S=BB,*TT=BB; inline int in(){ r int x; r bool f; r char c; for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-'); for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0'); return f?-x:x; } struct node{ int n,k,id; inline bool operator <(const node &b){ return k<b.k; } }q[MN]; ll T[M<<1],ans[MN];int n; inline void A(int k,int v){for (k+=M; k; k>>=1) T[k]+=v;} inline ll Q(int L,int R){ r ll res=0; for (L+=M-1,R+=M+1; L^R^1; L>>=1,R>>=1){ if (~L&1) res+=T[L^1]; if (R&1) res+=T[R^1]; }return res; } inline void update(int k){for (r int i=M/k; i; --i) A(i*k,i);} int main(){ n=in(); for (r int i=1; i<=n; ++i) q[i].n=in(),q[i].k=in(),q[i].id=i; std::sort(q+1,q+n+1);for (r int i=1,j=0; i<=n; ++i){ while(j<q[i].k) update(++j); ans[q[i].id]=Q(1,q[i].n); } for (r int i=1; i<=n; ++i) printf("%lld\n",ans[i]); }