zoukankan      html  css  js  c++  java
  • 2020-4-2模拟赛题解

    T1

    这道题直接纯模拟就好了,我们可以发现题意是这样的:

    • 如果这 44 个数有数字重复,答案为 Need fix.
    • 如果这 44 个数没有数字重复,答案为 44 个数中的最小数的编号
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int a[10],ans=INT_MIN,ai;
    int main(){
    	for(int i=1;i<=4;i++)read(a[i]);
    	for(int i=1;i+1<=4;i++)
    		for(int j=i+1;j<=4;j++)
    			if(a[i]==a[j]){//有重复
    				cout<<"Need fix.";//输出
    				return 0;
    			}
    	for(int i=1;i<=4;i++){
    		if(a[i]>ans){
    			ans=a[i];//答案
    			ai=i;//编号
    		}
    	}cout<<ai;
    	return 0;
    }
    

    T2

    这是一道找最小值的问题,我们把读入数据代入那个公式再对所有答案去一个 minmin 就行了,需要注意的是 infty 设成 INT_MAX 会错,需要设成 LONG_LONG_MAX

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int n,ai;
    double x,ans=LONG_LONG_MAX;//注意!
    int main(){
    	cin>>n>>x;
    	for(int i=1;i<=n;i++){
    		double c,t,e;
    		cin>>c>>t>>e;
    		double xx=c+t*x/e;
    		if(xx<ans){
    			ans=xx;
    			ai=i;
    		}
    	}cout<<ai;
    	return 0;
    }
    

    T3

    这道题用递归做非常方便,直接模拟就行了。

    ii11

    进的位数 == n÷xn div x

    这一位的数 == nmodxn mod x

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    ll n;
    void work(ll n,ll x){
    	if(n==0)return;
    	work(n/x,x+1);
    	cout<<n%x<<" ";
    }
    int main(){
    	read(n);
    	if(n==0)cout<<0;//这里需要特判
    	work(n,1);
    	return 0;
    }
    

    T4

    这道题又可以用递归做。

    我们发现培养之后就是斐波那契数,斐波那契数有下面几个性质:

    • 所有自然数都可以用不重复的斐波那契数组成。

    • 斐波那契数增长是指数级的。

    所以可以直接枚举天数。

    我们可以写一个 checkcheck 函数

    bool check(ll x,ll n){
    	if(x<=0)return false;
    	if(f[x]==n)return true;
    	if(f[x]>n)return check(x-1,n);//这次培养的话病体数量就超过了想要培养的总数了,所有不培养
    	return check(x-2,n-f[x]);//否则培养
    }
    

    其他就比较简单了:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    ll n,X=2,f[10010];
    bool check(ll x,ll n){
    	if(x<=0)return false;
    	if(f[x]==n)return true;
    	if(f[x]>n)return check(x-1,n);//这次培养的话病体数量就超过了想要培养的总数了,所有不培养
    	return check(x-2,n-f[x]);//否则培养
    }
    void print(ll x,ll n){
    	if(f[x]==n){
    		cout<<X-x+1<<" ";
    		return;
    	}
    	if(f[x]>n)print(x-1,n);
    	else{
    		cout<<X-x+1<<" ";
    		print(x-2,n-f[x]);
    	}
    }
    int main(){
    	read(n);
    	if(n==0){
    		cout<<endl<<0;//边界
    		return 0;
    	}
    	if(n==1){
    		cout<<1<<endl<<1;//边界
    		return 0;
    	}
    	f[0]=1;f[1]=1;//初始化
    	while(1){
    		f[X]=f[X-1]+f[X-2];
    		if(check(X,n)){
    			print(X,n);
    			cout<<endl<<X;
    			return 0;
    		}X++;
    	}
    	return 0;
    }
    

    T5

    首先我们可以枚举全排列。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int n,d,a[20],f[20],ans;
    int gcd(int x,int y){
    	if(y==0)return x;
    	return gcd(y,x%y);
    }
    int check(){
    	ll ans=0;
    	for(int i=1;i<=n;i++)ans+=abs(a[f[i]]-a[f[i-1]])*abs(a[f[i]]-a[f[i-1]]);
    	return ans;
    }
    int main(){
    	read(n);
    	for(int i=1;i<=n;i++)read(a[i]),f[i]=i;
    	do{
    		ans=max(ans,check());
    	}while(next_permutation(f+1,f+n+1));
    	cout<<ans;
    	return 0;
    }
    

    这样可以获得 5050 分的好成绩

    我们还可以状压 dpdp

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    ll n,d,a[15],dp[1<<20][20],ans;
    int main(){
    	read(n);
    	for(int i=1;i<=n;i++)read(a[i]);
    	for(int i=1;i<=n;i++)dp[(1<<(i-1))][i]=a[i]*a[i];
    	for(int i=1;i<(1<<n);i++){
    		for(int j=1;j<=n;j++){
    			if(i&(1<<(j-1))){
    				for(int k=1;k<=n;k++){
    					if(i&(1<<(k-1))&&k!=j){
    						dp[i][j]=max(dp[i][j],dp[i^(1<<(j-1))][k]+(a[j]-a[k])*(a[j]-a[k]));
    						ans=max(ans,dp[i][j]);
    					}
    				}
    			}
    		}
    	}cout<<ans;
    	return 0;
    }
    

    上面 22 个代码是我考试的时候对拍用的

    下面说正解了。

    我们发现最优解一定是放一个最大数,再一个最小数,再一个剩下的最大数,再一下剩下的最小数……

    证明的话,我觉得挺显然的qwq

    Q:那你还写对拍?!

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    ll n,a[110],ans;
    vector<ll>v;
    int main(){
    	read(n);
    	for(int i=1;i<=n;i++)read(a[i]);
    	a[++n]=0;
    	sort(a+1,a+n+1);
    	ll l=1,r=n;
    	while(l<=r){ 
    		if(l==r)v.push_back(a[l]);
    		else{
    			v.push_back(a[l]);
    			v.push_back(a[r]);
    		}l++;
    		r--;
    	}
    	for(int i=1;i<v.size();i++)ans=ans+abs(v[i]-v[i-1])*abs(v[i]-v[i-1]);
    	cout<<ans;
    	return 0;
    }
    

    T6

    这其实是一个背包 dpdp,挺裸的,主要是输出方案,这倒不算啥,关键是输出方案要符合字典序

    这里借用 apocryphal extcolor{black}{a} extcolor{red}{pocryphal} 大佬的方法,把序列翻转,这样就可以了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    ll n,x,t[5010],a[5010],f[5010][5010],xx[5010][5010],flag;
    void print(ll x,ll y){
    	if(x==0)return;
    	if(xx[x][y]){
    		if(flag==0)cout<<n-x+1,flag=1;
    		else cout<<" "<<n-x+1;
    	}print(x-1,y-xx[x][y]*t[x]);
    }
    int main(){
    	read(n);read(x);
    	for(int i=1;i<=n;i++)read(t[i]);
    	for(int i=1;i<=n;i++)read(a[i]);
    	reverse(t+1,t+n+1);
    	reverse(a+1,a+n+1);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=x;j++){
    			f[i][j]=f[i-1][j];
    			xx[i][j]=0;
    			if(j>=t[i]){
    				if(f[i-1][j-t[i]]+a[i]>=f[i][j]){
    					xx[i][j]=1;
    					f[i][j]=f[i-1][j-t[i]]+a[i];
    				}
    			}
    		}
    	}
    	if(f[n][x]==0){
    		puts("Develop failed.");
    		return 0;
    	}print(n,x);
    	return 0;
    }
    

    T7

    首先给出我不剪枝的代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int n,d,a[20],f[20],ans;
    int gcd(int x,int y){
    	if(y==0)return x;
    	return gcd(y,x%y);
    }
    bool check(){
    	for(int i=1;i<n;i++)
    		if(gcd(a[f[i]],a[f[i+1]])!=1||abs(a[f[i]]-a[f[i+1]])>d)return false;
    	return true;
    }
    int main(){
    	read(n);read(d);
    	for(int i=1;i<=n;i++)read(a[i]),f[i]=i;
    	do{
    		if(check())	ans++;
    	}while(next_permutation(f+1,f+n+1));
    	cout<<ans;
    	return 0;
    }
    

    只有 4040qwqqwq。。。

    再给出减枝后的代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int n,d,ans,h[25],f[25],a[25];
    vector<int>v[25];
    int gcd(int x,int y){
    	if(x%y==0)return y;
    	return gcd(y,x%y);
    }
    void dfs(int x){
    	if(x==n+1){
    		ans++;
    		return;
    	}
    	for(int i:v[f[x-1]]){
    		if(!h[i]&&abs(a[f[x-1]]-a[i])<=d){
    			h[i]=1;
    			f[x]=i;
    			dfs(x+1);
    			h[i]=0;
    		}
    	}
    }
    int main(){
    	read(n);read(d);
    	for(int i=1;i<=n;i++)read(a[i]);
    	for(int i=1;i<n;i++)
    		for(int j=i+1;j<=n;j++)
    			if(gcd(a[i],a[j])==1)v[i].push_back(j),v[j].push_back(i);
    	for(int i=1;i<=n;i++){
    		h[i]=1;
    		f[1]=i;
    		dfs(2);
    		h[i]=0;
    	}cout<<ans;
    	return 0;
    }
    

    这时,你拿到了 9090 分的好成绩。

    之后想到 100100 分的话,我是听了 apocryphal extcolor{black}{a} extcolor{red}{pocryphal} 大佬的建议,写了复杂度有保证的装压 dpdp,时间复杂度 O(2nn2)O(2^n n^2),比搜索的 O(n!)O(n!) 确实强了不少

    十分好写,但需要一定的装压 dpdp 的基础

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int n,d,a[15],dp[1<<20][20],ans;
    int gcd(int x,int y){
    	if(x%y==0)return y;
    	return gcd(y,x%y);
    }
    int main(){
    	read(n);read(d);
    	for(int i=1;i<=n;i++)read(a[i]);
    	for(int i=1;i<=n;i++)dp[(1<<(i-1))][i]=1;
    	for(int i=1;i<(1<<n);i++){
    		for(int j=1;j<=n;j++){
    			if(i&(1<<(j-1))){
    				for(int k=1;k<=n;k++){
    					if(i&(1<<(k-1))&&k!=j){
    						if(abs(a[j]-a[k])<=d&&gcd(a[j],a[k])==1){
    							dp[i][j]+=dp[i^(1<<(j-1))][k];
    						}
    					}
    				}
    			}if(i==(1<<n)-1)ans+=dp[i][j];
    		}
    	}cout<<ans;
    	return 0;
    }
    

    Q:我卡常搜索过了!

    A:那是数据水了。

    PS:这道题的数据水了,不信你给你的程序测下面的这组数据

    12 2
    1 1 1 1 1 1 1 1 1 1 1 1
    

    你会发现标程过不了,因为 12!=47900160012!=479001600‬ 远超 11 亿。

    T8

    这道题的 IdeaIdea 不错,可是问题有点多啊

    1. 样例,毒瘤的出题人手抖了一下,把第 44 行的那个 11 打到了第 33 行,变成了

      3 2
      2 4
      21
      1
      2 4
      1 1 10 10 
      

      使本来就比较难懂的题目更加难懂,这里把我坑了好久。。。

    2. 数据范围,毒瘤的出题人手抖了一下,吧 10510^5打成了 105105。。。

    3. 数据,毒瘤的出题人没有考虑到读入数据中第 ii 层医疗机构能管辖到的最大编号应该是单调递增的

    4. 标程,毒瘤的出题人没有设置边界条件 s1=1s_1=1

    说思路:

    这题应该是一眼二分的,考虑如何 checkcheck

    考虑贪心

    • 如果当前医护人员还可以再去往下一个基层医疗机构就让他去
    • 如果当前医护人员去不了下一个基层医疗机构就新派一个医护人员去

    现在要做的就是快速算出两点的距离,很明显这道题的医疗机构的连接是树形的,也就是说所有医疗机构组成看一棵树,求树上任意 22 点路径,很容易想到 LCALCA,如果你不会 LCALCA 的话,可以先学一下

    bool check(int x){
    	int k=0,m=1,a=1;
    	for(int i=s[D-1]+1;i<=s[D];i++){
    		int ans=dist(a,i);//路径长度
    		if(k+ans<=x)k+=ans;//我们的贪心思想
    		else{
    			m++;//新派一个医护人员
    			k=d[i];//这个是根节点到i的距离
    			if(k>x)return false;//如果的医护人员在不了x的时间内去不了i号点。
    		}a=i;//更新
    	}return m<=::m;//医护人员数量需要小于等于现有医护人员的数量
    }
    

    关于 distdist

    void dfs(int x,int fa){
    	::fa[x][0]=fa;
    	dep[x]=dep[fa]+1;
    	d[x]+=d[fa];
    	for(int i=1;i<=20;i++)::fa[x][i]=::fa[::fa[x][i-1]][i-1];
    	if(x>s[D-1])return;
    	for(int i=l[x];i<=r[x];i++)dfs(i,x);
    }
    int lca(int a,int b){
        if(dep[a]>dep[b])swap(a,b);
        for(int i=20;i>=0;i--)
            if(dep[a]<=dep[b]-(1<<i))b=fa[b][i];
        if(a==b)return a;
        for(int i=20;i>=0;i--)
            if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
        return fa[a][0];
    }
    int dist(int a,int b){
    	int k=lca(a,b);
    	return d[a]+d[b]-2*d[k];
    }
    

    很普通的倍增 LCALCA

    现在就放一下总代码吧:

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    const int N=1e5+10;
    int sz[N],l[N],r[N],D,m,x,fa[N][25],dep[N],s[N],d[N];
    void dfs(int x,int fa){
    	::fa[x][0]=fa;
    	dep[x]=dep[fa]+1;
    	d[x]+=d[fa];
    	for(int i=1;i<=20;i++)::fa[x][i]=::fa[::fa[x][i-1]][i-1];
    	if(x>s[D-1])return;
    	for(int i=l[x];i<=r[x];i++)dfs(i,x);
    }
    int lca(int a,int b){
        if(dep[a]>dep[b])swap(a,b);
        for(int i=20;i>=0;i--)
            if(dep[a]<=dep[b]-(1<<i))b=fa[b][i];
        if(a==b)return a;
        for(int i=20;i>=0;i--)
            if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
        return fa[a][0];
    }//倍增LCA
    int dist(int a,int b){
    	int k=lca(a,b);
    	return d[a]+d[b]-2*d[k];//距离
    }
    bool check(int x){
    	int k=0,m=1,a=1;
    	for(int i=s[D-1]+1;i<=s[D];i++){
    		int ans=dist(a,i);//路径长度
    		if(k+ans<=x)k+=ans;//我们的贪心思想
    		else{
    			m++;//新派一个医护人员
    			k=d[i];//这个是根节点到i的距离
    			if(k>x)return false;//如果的医护人员在不了x的时间内去不了i号点。
    		}a=i;//更新
    	}return m<=::m;//医护人员数量需要小于等于现有医护人员的数量
    }
    int main(){
    	read(D);read(m);
    	l[0]=1,r[0]=1;
    	s[1]=sz[1]=1;
    	for(int i=2;i<=D;i++)read(sz[i]),s[i]=s[i-1]+sz[i];
    	for(int i=1;i<D;i++){
    		for(int j=1;j<=sz[i];j++){
    			read(r[s[i-1]+j]);
    			r[s[i-1]+j]+=s[i];
    			l[s[i-1]+j]=r[s[i-1]+j-1]+1;
    		}
    		for(int j=1;j<=sz[i];j++)
    			for(int k=l[s[i-1]+j];k<=r[s[i-1]+j];k++)
    				read(d[k]);//连接k好点和他父亲节点的边的长度
    	}
    	dfs(1,0);//LCA的预处理啦
    	int l=0,r=INT_MAX;//二分
    	while(l+1<r){
    		int mid=(l+r)>>1;
    		if(check(mid))r=mid;
    		else l=mid;
    	}cout<<r;//输出
    	return 0;
    }
    

    真的的佩服 apocryphal extcolor{black}{a} extcolor{red}{pocryphal} 大佬,如果不是最后一题数据的问题的话,就直接切掉了

  • 相关阅读:
    测试学习使用
    Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modules
    web框架之Spring-MVC环境搭建
    serialVersionUID
    在eclipse中部署发布web项目 和 更改eclipseweb项目发布的路径
    servlet HttpSession 监听器
    java web 中的转发和重定向
    Spring MVC 中的 forward 和 redirect
    各个部门英语标识
    添加滚动条,内容多时显示,内容少时隐藏
  • 原文地址:https://www.cnblogs.com/zhaohaikun/p/12816961.html
Copyright © 2011-2022 走看看