【20170528校内模拟赛】
学长(省队dalao)所出的模拟赛,个人感觉题目难度还是比较适中的,难度在Noip提高组左右,部分题可能比较接近弱省省选,总体来讲试题考查范围较广,个人认为还是很不错的。
所有试题如无特殊声明,不开启任何优化,时限1s,内存上限为128MB
题目还是挺可爱的,本人太弱,CD没怎么想出来......F题网络流跑的飞慢。。。365/600
T1(YYH算组合数)
Description
YYH手上有一个长度为N的数列,而且这个数列正好能表示为\(\{C_{2n}^{1},C_{2n}^{3},C_{2n}^{5},...,C_{2n}^{2n-1}\}\)。现在他想知道这个数列的最大公约数是多少,请你帮帮他
Input
每个数据点包括多组数据,以EOF结束
对于每个数据输入一行一个整数,N,为数列的长度
Output
对于每个数据输出一行一个整数,为数列的最大公约数
Sample Input
1
2
Sample Output
2
4
Hint
\(1 \leq n \leq 2^{31}-1\),每个数据点数据不超过10个
Solution
结论题,通过推导或打表,显然Ans就是\(lowbit(2n)\),所以很水了啦...时间效率O(T)
Code
#include <stdio.h>
#define ll long long
int main(){
ll n;
while(~scanf("%lld",&n))
printf("%lld\n",(n&(-n))<<1);
}
T2(YYH的王国)
Description
YYH拥有一个有n个城市的国家,编号为1~n。其中城市i到城市j的路径长度为i和j的最小公倍数。现在YYH想建一些高速公路,使得任意两座城市都能通过高速公路直接或间接到达。建造一条高速公路的代价为这条高速公路的长度,他想知道最小代价是多少?
Input
第一行输入一个整数,T,表示有T组数据。
接下来T行每行输出一个整数n,表示有n个国家
Output
输出T行,每行一个数字表示最小代价
Sample Input
2
1
3
Sample Output
0
5
Hint
\(1\leq T \leq 100,1 \leq n \leq 10^9\)
Solution
结论题2号,容易发现所有点向1号点连边费用一定最小,故\(Ans=\Sigma_{i=2}^{n} i\),使用数学公式计算即可,时间效率O(T).
Code
#include <stdio.h>
#define r register
#define ll long long
inline ll read(){
r ll 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;
}ll ans,n,T;
int main(){
T=read();while(T--){
n=read();ans=((n+1)*n)/2-1;
printf("%lld\n",ans);
}
}
T3(YYH的积木)
Description
YYH手上有n盒积木,每个积木有个重量。现在他想从每盒积木中拿一块积木,放在一起,这一堆积木的重量为每块积木的重量和。现在他想知道重量最少的k种取法的重量分别是多少。
Input
第一行输入一个整数T,表示有T组数据
每组数据的第一行输入两个整数,n,k,意义如题目所描述。
每组数据接下来的n行,第一个整数为\(m_{i}\),表示第i盒积木的数量,在同一行有\(m_{i}\)个整数,分别表示每个积木的重量
Output
输出T行,分别为每组数据的答案
Sample Input
1
3 10
5 1 2 3 4 5
3 1 9 7
4 1 2 3 5
Sample Output
3 4 4 5 5 5 6 6 6 7
Hint
\(1\leq T \leq 10,2\leq m_{i} \leq 100 ,1\leq n,k \leq 100\)
每个积木的重量为不超过10000的正整数,保证$$\prod_{i=1}^{n} m_{i} \geq k$$
Solution
显然本题是可以DP做的,我们可以发现:
对于一个新来的一组,我们考虑对之前若干组中最优的k个进行选择并加上这个新数,这样进行之后再排序是可以解决问题的,同时我们可以发现这是满足最优子结构性质的。
如此反复操作即可实现整体最优,考虑使用滚动数组加heap优化这个过程即可通过。
总复杂度:\(O(T k (\log_{2} k) \Sigma m_{i})\) / \(O(max(m_{i}))\).
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define r register
#ifndef Debug
#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;
#endif
using namespace std;
int T,n,k,m,h[2][105],len[2],a[105];
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;
}
inline void work(){
n=read(),k=read();memset(h,0,sizeof(h));
len[0]=1; for (r int i=1; i<=n; ++i){
len[i&1]=0;m=read();
for (r int j=1; j<=m; ++j){
a[j]=read();
for (r int l=1; l<=len[~i&1]; ++l){
r int tmp=a[j]+h[~i&1][l];
if (len[i&1]<k){
h[i&1][++len[i&1]]=tmp;
push_heap(h[i&1]+1,h[i&1]+len[i&1]+1);
}else if (tmp<h[i&1][1]){
pop_heap(h[i&1]+1,h[i&1]+len[i&1]+1);
h[i&1][len[i&1]]=tmp;
push_heap(h[i&1]+1,h[i&1]+len[i&1]+1);
}
}
}
}sort(h[n&1]+1,h[n&1]+len[n&1]+1);
for (r int i=1; i<k; ++i) printf("%d ",h[n&1][i]);
printf("%d\n",h[n&1][k]);
}
int main(){T=read(); while(T--) work();}
T4(YYH的苍天大竹)
Description
YYH擅长种竹子。今天他收获了一根竹子,准备将这根柱子卖给CHS。这个竹子有n-1个竹节。CHS要求一定要从竹节的地方砍,而且砍成若干段后每一段竹子中最长的一小段竹子和最短的一小段的长度差不能超过s,一段竹子至少含有l小段竹子。这可让YYH郁闷了,他希望留点力气刷题,所以他想知道他最少可以将整根竹子砍成多少段。
Input
输入第一行三个整数,n,s,l,意义如题目所描述
输入第二行n个整数,a[1],a[2],……,a[n],分别为每小段竹子的长度
Output
如果有满足条件的砍法,输出整根竹子砍成最少的段数。否则输出-1
Sample Input
7 4 2
2 6 2 4 8 2 4
Sample Output
3
Hint
样例解释
样例其中一种可行的砍法:1-3一段,4-5一段,6-7一段
\(1\leq n \leq 10^{5} ,0\leq s \leq 10^9 , 1\leq l \leq 10^5 ,-10^9 \leq a[i] \leq 10^9\)
Solution
又是一道DP题,首先通过观察我们可以发现一个竹节最远可以与之前共处与一段内的情况是满足区间单调性的。
换而言之这是可以用单调队列来得出的,我们记一个竹节i可以到的最左端(标号最小的)竹节的标号为minto[i],使用线段树与单调队列得出。
接下来我们可以容易得到状态转移方程\(f_{i}=min(f_{minto[i]-1}\)~\(f_{i-l})+1\).
可以发现上式中的min可以使用线段树优化,然后minto数组可以直接预处理得出。于是我们就解决了这题。
总复杂度为\(O(n\log_{2}n)\) / $ O(n)$.
Code
#include <stdio.h>
#define MN 100005
#define MM (1<<17)
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define inf 0x3f3f3f3f
#define rint register int
#ifndef Debug
#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;
#endif
inline int read(){
rint x; register bool f; register 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 mi[MM<<1],ma[MM<<1],f[MM<<1],l[MN],n,s,len,M;
inline void ad(int k,int v){for (f[k+=M]=v,k>>=1; k; k>>=1) f[k]=min(f[k<<1],f[k<<1|1]);}
inline int Qmin(int *T,int l,int r){
rint res=inf;
for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
if (~l&1) res=min(res,T[l^1]);
if ( r&1) res=min(res,T[r^1]);
}return res;
}
inline int Qmax(int l,int r){
rint res=-inf;
for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
if (~l&1) res=max(res,ma[l^1]);
if ( r&1) res=max(res,ma[r^1]);
}return res;
}
void init(){
n=read()+1,s=read(),len=read();for (M=1; M<n+2; M<<=1);
for (rint i=1; i<=M+n+1; ++i) f[i]=mi[i]=inf,ma[i]=-inf;
for (rint i=M+2; i<=M+n; ++i) mi[i]=ma[i]=read();mi[M+1]=ma[M+1]=mi[M+2];
for (rint i=M-1; i; --i) mi[i]=min(mi[i<<1],mi[i<<1|1]),ma[i]=max(ma[i<<1],ma[i<<1|1]);
}
void solve(){
rint le=1;for (rint i=1; i<=n; ++i){
rint tmp=Qmax(le,i)-Qmin(mi,le,i);
while(tmp>s) ++le,tmp=Qmax(le,i)-Qmin(mi,le,i);l[i]=le;
}ad(1,0);for (rint i=2; i<=n; ++i)
if (max(1,l[i]-1)<=i-len)
ad(i,Qmin(f,max(1,l[i]-1),i-len)+1);
printf("%d",(f[M+n]>=inf?-1:f[M+n]));
}
int main(){init(); solve(); return 0;}
T5(YYH的营救计划)
Description
“咚咚咚……”“查水表!”原来是查水表来了,现在哪里找这么热心上门的查表员啊!YYH感动的热泪盈眶,开起了门……
YYH的父亲下班回家,街坊邻居说YYH被一群陌生人强行押上了警车!YYH的父亲丰富的经验告诉他YYH被带到了t区,而自己在s区。
该市有m条大道连接n个区,一条大道将两个区相连接,每个大道有一个拥挤度。YYH的父亲虽然很着急,但是不愿意拥挤的人潮冲乱了他优雅的步伐。所以请你帮他规划一条从s至t的路线,使得经过道路的拥挤度最大值最小。
Input
第一行四个数字n,m,s,t。
接下来m行,每行三个数字,分别表示两个区和拥挤度。
(有可能两个区之间有多条大道相连。)
Output
输出题目要求的拥挤度。
Sample Input
3 3 1 3
1 2 2
2 3 1
1 3 3
Sample Output
2
Hint
\(1\leq n \leq 2*10^{5} ,m \leq 2n,拥挤度\leq 10000\)
保证\(1\leq s,t\leq t,s!=t\),s与t联通。
样例解释:
YYH的父亲要从1号点去3号点,最优路线为1->2->3。
Solution
显然是一道MST题,注意判断一下如果S与T父亲相同直接退出返回答案即可。时间效率\(O(m \log_{2} m)\).
Code
#include <stdio.h>
#include <algorithm>
#define r register
#define ll long long
#define MN 200005
#define max(a,b) ((a)>(b)?(a):(b))
#ifndef Debug
#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;
#endif
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;
}
struct node{
int x,y,val;
inline bool operator <(const node &b)const{
return val<b.val;
}
}edge[MN<<1];
int fa[MN],S,T,n,m,cnt,k;
inline int getfa(int x) {return fa[x]?fa[x]=getfa(fa[x]):x;}
void init() {
n=read(),m=read(),S=read(),T=read();
for (r int i=1; i<=m; ++i) {
r int x=read(),y=read(),val=read();
edge[i].x=x,edge[i].y=y,edge[i].val=val;
}std::sort(edge+1,edge+m+1);
}
void solve(){
for (r int i=1; i<=m; ++i)
if (getfa(edge[i].x)!=getfa(edge[i].y)) {
++k;fa[getfa(edge[i].x)]=getfa(edge[i].y);
if (getfa(S)==getfa(T)){printf("%d",edge[i].val); break;}
if (k==n-1) break;
}
}
int main() {init();solve();return 0;}
T6(YYH的球盒游戏)
Description
YYH有一些总共有\(n\)种颜色的球,他有\(i\)颜色的球\(a_{i}\)个。他同样有\(m\)个盒子,第\(j\)个盒子能放\(c_{j}\)个球。
他的目标是把这个球按规则放进个盒子里:
- 对于一个盒子\(j\),对于每种颜色的球至多只能放\(1\)个.
- 把颜色为\(i\)的球放进盒子\(j\),他能获得的收益为\(b_{i,j}\)。
- 由于盒子有一定的额外承受能力,所以在最后,对于一个盒子\(j\),如果里面的球的数量比\(c_{j}\)多了\(x\),那么YYH会有\(x^2\)的花费。
YYH不需要把每个球都放到盒子里,他只希望他的收益与花费之差最大。
Input
第一行输入两个整数,\(n,m\),为球颜色个数和盒子个数。
第二行输入\(n\)个整数,\(a_{1},a_{2},...,a_{n}\),分别表示每种颜色小球的个数
第三行输入\(m\)个整数,\(c_{1},c_{2},...,c_{m}\),分别表示每个盒子的基础承载能力。
接下来的\(n\)行,每行\(m\)个数,第\(i\)行第\(j\)个数为\(b_{i,j}\),表示将颜色为\(i\)的球放进盒子\(j\)的收益。
Output
输出一个整数为YYH最大的收益与花费之差。
Sample Input
2 2
1 1
0 2
1 7
3 1
Sample Output
9
Hint
\(1\leq n,m \leq 100,1\leq a_{i} \leq 100 ,0 \leq c_{i} \leq 100,0\leq b_{i,j} \leq 10^3\)
Time Limit Per Test Case:3s /Memory Limit:256MB.
Solution
这是一道费用流的简单模型题。
- 首先源点向所有颜色连一条费用为0,容量为\(a_{i}\)的边.
- 接下来所有球盒向汇点连一条费用为0,容量为\(c_{j}\)的边.
- 接下来所有颜色向所有球盒连一条费用为-\(b_{i,j}\),容量为1的边.
然后我们发现事情并没有这么简单(笑)——可以花费费用来获得额外容量......,而且是二次方级别的增长......,于是我们就只能差分来连边了......
- 所有球盒向汇点连\(n-c_{j}\)条边,第\(i\)条边费用为\(2i-1\),容量为1.
然而又出现了问题(摊手)......球可以不用完。。。。所以我们只好接着加边了......
- 所有颜色向汇点连费用为0,容量为inf的边。。。
于是我们就可以愉快的在建好的图上跑费用流了哈哈哈哈!!!(一点都不愉快)
本以为就这么简单,结果本人费用流不够优秀......TLE60%orz......难受......
于是在学长的指导下明白了网络流倒着搜更优秀这个道理......Ditoly 清华爷orz......
丧病出题人啊......orz...
时间效率\(O(costflow(n+m,2nm))\)。
Code
#include <stdio.h>
#include <string.h>
#define r register
#define inf 0x3f3f3f3f
#define ME 30005
#define MN 305
#define S 0
#define T 301
#define min(a,b) ((a)<(b)?(a):(b))
#ifndef Debug
#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;
#endif
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;
}
int to[ME<<1],nxt[ME<<1],v[ME<<1],c[ME<<1];
int head[MN],dis[MN],que[MN],cost,iter[MN];
int n,m,cnt=1;bool vis[MN];
inline void ins(int x,int y,int val,int cost){to[++cnt]=y,nxt[cnt]=head[x],v[cnt]=val,c[cnt]=cost;head[x]=cnt;}
inline void insw(int x,int y,int v,int c){ins(x,y,v,c);ins(y,x,0,-c);}
inline bool SPFA_flow(){
memset(dis,0x3f,sizeof(dis));
r int h=0,t=1; que[1]=T;
vis[T]=1;dis[T]=0;
while(h!=t){
r int u=que[(++h)%=MN];
for (r int i=head[u]; i; i=nxt[i])
if (v[i^1]&&dis[to[i]]>dis[u]+c[i^1]){
r int V=to[i];dis[V]=dis[u]+c[i^1];
if (!vis[V]){
if (dis[V]<dis[h+1]) que[h]=V,h=(h-1+MN)%MN;
else que[(++t)%=MN]=V;
vis[V]=1;
}
}
vis[u]=0;
}
memcpy(iter,head,sizeof(head));
return dis[S]!=inf;
}
int dfs(int u,int f){
vis[u]=1;if (u==T) return f;r int used=0;
for (r int &i=iter[u]; i; i=nxt[i])
if (v[i]&&!vis[to[i]]&&dis[to[i]]==dis[u]-c[i]){
r int w=dfs(to[i],min(f-used,v[i]));
used+=w;v[i]-=w,v[i^1]+=w;cost+=c[i]*w;
if (used==f) return f;
}
return used;
}
inline void costflow(){
while(SPFA_flow())do memset(vis,0,sizeof(vis)); while(dfs(S,inf));
printf("%d",-cost);
}
inline void init(){
n=read(),m=read();
for (r int i=1; i<=n; ++i)
insw(S,i,read(),0),insw(i,T,inf,0);
for (r int i=1; i<=m; ++i){
r int v=read();
insw(i+n,T,v,0);
for (r int j=1; j<=n-v; ++j)
insw(i+n,T,1,j*2-1);
}
for (r int i=1; i<=n; ++i)
for (r int j=1; j<=m; ++j)
insw(i,j+n,1,-read());
}
int main(){init(); costflow(); return 0;}