蓝桥杯14届A组题解(部分)

文章讨论了如何通过编程实现数字奇偶性判断、深度优先搜索(DFS)、区间动态规划以及最小生成树(包括克鲁斯卡尔算法和LCA)等IT技术在解决算法问题中的应用,涉及网络稳定性分析和异或操作的优化策略。
摘要由CSDN通过智能技术生成

A 幸运数

可以先判断这个数字的长度是奇数还是偶数,如果是奇数的话,就肯定不是了,如果是偶数的话,就有可能是,再次进行判断

先判断奇偶,可以把数字转化成字符串判断

bool check1(int x){
    string s = to_string(x);
    if(s.size()&1) return false;
    else return true;
}

接下来对偶数进行判断

bool check2(int x){
    string s = to_string(x);
    int n =s.size();
    n = n/2;
    int sum1 =0 , sum2 = 0;
    for(int i =0;i<n;++i){
        sum1 +=s[i] -'0';
    }
    for(int i =n;i<n*2;++i)sum2 += s[i] -'0';
    return sum1 == sum2;
}

B 有奖问答

可以直接dfs搜索

void dfs(int x , int sum){
    if(sum == 70){
        ans++;
        return;
    }
    if(sum == 100 || x ==31)return;
    
    dfs(x+1 , sum+10);
    dfs(x+1 , 0);
    
}

D 更小的数

C题不会写,先略过

D题的话可以用区间dp,但是不需要On2的暴力算法已经可以直接过了。

bool check(int l ,int r){
	for(int i = l , j = r;i<=r&&i<j;++i,--j){
		if(s[j] < s[i])return true;
		else if(s[j] > s[i])return false;
	}
	return false;
}


void solve(){
  cin>>s;
  int n =s.size();
  for(int i =0;i<n;++i){
    for(int j = i+1;j<n;++j){
      if(check(i,j))cnt++;
    }
  }
  cout<<cnt;


}

不解释了 ,很简单的一道题

F 买瓜

这道题的话,暴搜+剪枝可以过,复杂度是小于3的n次方的。

剪枝的话需要思考如何剪枝?

1、当sum > m的时候可以直接剪

2、当已经买完最后一个瓜之后,可以直接剪

3、如果当前的sum加上剩下的瓜在不劈瓜的前提下,总和也小于m的话,可以直接剪。

4、由于是求最小值,我们维护一个最小值ans , 如果当前劈瓜次数cnt已经>=ans,剪枝

那么对于3,该怎么剪枝呢,我们可以维护一个后缀和来求,另外,我们利用贪心的思想,把瓜从大到小排列。

void dfs(int x , double sum , int cnt){
	if(sum == m){
		ans = min(ans , cnt);
	}
	if(sum > m)return;
	if(x == n+1)return;
	if(cnt >= ans)return;
	if(sum + pre[x] < m)return;

	dfs(x+1 , sum + a[x] ,cnt);
	dfs(x+1 , sum + a[x]/2.0 , cnt+1);
	dfs(x+1 ,sum ,cnt);

}



void solve(){
	cin>>n>>m;
	for(int i =1;i<=n;++i)cin>>a[i];
	sort(a+1 ,a+1+n , greater<int>());
	for(int i = n;i>=0;--i)pre[i] = pre[i+1]+a[i]; //后缀和

	dfs(1 , 0.0 , 0);
	if(ans == INF) cout<<-1;
    else cout<<ans;
}

G 网络稳定性

这道题其实上就是最小生成树 + lca。利用克鲁斯卡尔重构树和树链剖分的方法即可解决

1、克鲁斯卡尔重构树

struct edge{
    int x,  y , w;
}e[M];

int find(int x){
    return pre[x] = pre[x] == x?x:find(pre[x]);
}

bool cmp(edge a , edge b){
    return a.w > b.w;
}
void kruskal(){
    int id = n;
    for(int i =1;i<=m;++i){
        int x = e[i].x , y = e[i].y , w = e[i].w;
        int fx = find(x) , fy = find(y);
        if(fx  == fy)continue;  //并查集 , 如果两个点在一个集合,就跳过
        id++;
        pre[fx] = id;
        pre[fy] = id;
        g[id].push_back(fx);
        g[id].push_back(fx);
        val[id] = w;
    }
    
}




void solve(){
    cin>>n>>m>>q;
    for(int i =1;i<=m;++i){
	    cin>>e[i].x>>e[i].y>>e[i].w;
   }
    sort(e+1,e+1+m,cmp);//要求最大生成树,按照边权从大到小排列
    for(int i =1;i<=2*n;++i)pre[i] = i;//并查集
    kruskal();
    
}

2、树链剖分

void dfs1(int u ,int father){
    dep[u] = dep[father] + 1;
    sz[u] = 1;
    fa[u] = father;
    for(auto v:g[u]){
        if(v == father)return;
        dfs1(v,u);
        sz[u] +=sz[v];
        if(sz[son[u]] < sz[v])son[u] = v;
    }
}
void dfs2(int u ,int t ){
    top[u] = t;
    if(!son[u])return;
    dfs2(son[u] , t);
    for(auto v : g[u]){
        if(v == fa[u] || v == son[u])continue;
        dfs2(v , v);
    }
}

int lca(int u ,int v){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]])swap(u,v);
        u = fa[top[u]];
    }
    return dep[u] < dep[v] ? u : v;
}

for(int i = 1;i<=id;++i){
    if(pre[i] == i){ //找到根
        dfs1(i , 0);
        dfs2(i , i); 
    }
}

完整代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
// #define x first
// #define y second 
// #define int long long
// #define ll long long
const int mod = 998244353;
// const int mod = 80112002;
const int N = 4e5+9;
const int M = 3e5+9;
int n,m,q , id;
int val[N];
int pre[N]; 

struct edge{
	int x , y , w;
}e[M];

int fa[N] , dep[N] , sz[N] , son[N] ,top[N];
vector<int>g[N];

bool cmp(edge a , edge b){
	return a.w > b.w;
}

int find(int x){
	return pre[x] = pre[x] ==x ? x : find(pre[x]);
}

void kruskal(){
	id = n;
	for(int i =1;i<=m;++i){
		int x = e[i].x , y = e[i].y , w = e[i].w;
		int fx = find(x) ,fy = find(y);
		if(fx == fy)continue;
		id++;
		pre[fx] = id;
		pre[fy] = id;
		g[id].push_back(fx);
		g[id].push_back(fy);
		val[id] = w;
	}
	
}

void dfs1(int u , int father){ //求出son , sz
	fa[u] =father , dep[u] = dep[father]+1;
	sz[u] = 1;
	for(auto v:g[u]){
		if(v == father)continue;
		dfs1(v , u);
		sz[u] +=sz[v];
		if(sz[son[u]] < sz[v])son[u] = v;
	}
}

void dfs2(int u ,int t){  //求顶点
	top[u] = t;
	if(!son[u])return;
	dfs2(son[u] , t);
	for(auto v : g[u]){
		if(v == fa[u] || v == son[u])continue;
		dfs2(v,v);
	}
}

int lca(int u ,int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u,v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}


void solve(){
   cin>>n>>m>>q;
   for(int i =1;i<=m;++i){
	cin>>e[i].x>>e[i].y>>e[i].w;
   }
    for(int i =1;i<=n*2;++i)pre[i] = i;
    sort(e+1 , e+1+m ,cmp);
   kruskal();//克鲁斯卡尔重构树
   //树链剖分求lca
   for(int i =1;i<=id;++i){
		if(pre[i] == i){
			dfs1(i , 0);
			dfs2(i , i);
		}
   }

	while(q--){
		int a , b;
		cin>>a>>b;
		if(find(a) != find(b))cout<<-1<<endl;
		else cout<<val[lca(a,b)]<<endl;
		
	}

}




signed main(){
    ios::sync_with_stdio(false);
    cout.tie(0);
    cin.tie(0);
    int _ = 1;
    while(_--){
      solve();
    }
    
    return 0;
}

H 异或和之和

暴力法

比较推荐用暴力法,On2,能拿百分之60的分数,只需要用异或和就可以了

for(int i=1;i<=n;++i)cin>>a[i];
   for(int i =1;i<=n;++i)pre[i]  =pre[i-1]^a[i];
   for(int i =1;i<=n;++i){
       for(int j =i;j<=n;++j){
           ans +=pre[j]^pre[i-1];
       }
   }

贡献法

考虑每个数字每一位的贡献,对于几个数字中,他们的二进制,假如说第x位有3个1,2个0,不用考虑0,如果   1的数量是奇数,那么异或起来,这一位的贡献就是1<<x , 从左向右,假如某一位是0111,对于第四个,前面有两个1,他可以根两个1组合,也可以自己组合,变成数量为奇数,那么贡献就×前面的偶数数量+1,假如011的话,考虑最后一个1,就只有一种方式组成奇数数量,那么就是贡献*前面的奇数数量。

for(int i=1;i<=n;++i)cin>>a[i];

	for(int i =0;i<=19;++i){
		int s = 0 , n1 = 0 , n0 =1;
		for(int j=1;j<=n;++j){
			int bit = (a[j] >> i) & 1;
			s += bit;
			if(s&1){
				ans +=(1 << i) * n0;
				n1 ++ ;
			}
			else {
				ans +=(1 << i) * n1;
				n0++;
			}
		}
	}

完整:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
#define x first
#define y second 
#define int long long
// #define ll long long
const int mod = 998244353;
// const int mod = 80112002;
typedef pair<int,int> PII;
int n,m;
const int N =1e5+9;
int ans;
int a[N] , pre[N] ;
priority_queue<PII , vector<PII>,greater<PII>>q; 



void solve(){
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i];

	for(int i =0;i<=19;++i){
		int s = 0 , n1 = 0 , n0 =1;
		for(int j=1;j<=n;++j){
			int bit = (a[j] >> i) & 1;
			s += bit;
			if(s&1){
				ans +=(1 << i) * n0;
				n1 ++ ;
			}
			else {
				ans +=(1 << i) * n1;
				n0++;
			}
		}
	}
	cout<<ans;
}





signed main(){
    ios::sync_with_stdio(false);
    cout.tie(0);
    cin.tie(0);
    int _ = 1 ;
	// cin>>_;
    while(_--){
      solve();
    }
    
    return 0;
}
    

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值