2019 蓝桥杯国赛 B 组模拟赛 题解

标签 ok
2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

#include using namespace std; /* 求阶乘 去除尾部0 每次求阶乘时:结果去除尾0,并对 1e6取余 */typedef long long ll; ll n = 1325476; const ll mod = 1e6; ll ans = 1; void solve(){ while(ans%10 == 0) ans = ans/10; ans = ans%mod; }int main(){ for(ll i = n; i>=1; i--){ ans = ans * i; solve(); } printf("%lld",ans); return 0; } //137664

#include using namespace std; /* 2 * 5会产生0 做法:每次循环求阶乘时 统计2和5个数 并去除2和5,最后乘回来即可 */typedef long long ll; ll n = 1325476; const ll mod = 1e6; ll ans = 1; ll cnt1 = 0; ll cnt2 = 0; int main(){ for(ll i = 1; i <= n; i++){ ll x = i; while(x%2==0) cnt1++,x=x/2; while(x%5==0) cnt2++,x=x/5; ans = ans * x % mod; } if(cnt1 - cnt2 > 0){ for(int i=1; i<=cnt1-cnt2; i++) ans = ans * 2 % mod; }else{ for(int i=1; i<=cnt2-cnt1; i++) ans = ans * 5 % mod; } printf("%lld\n",ans); return 0; } //137664

101串 ok
2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

dfs搜索方法
#include using namespace std; int num[35]; long long ans = 0; /* dfs枚举2^30种可能结果 运行100秒左右 */void dfs(int k){ if(k==31){ for(int i=1; i<=28; i++){ if(num[i] == 1 && num[i+1] == 0 && num[i+2] == 1){ ans++; break; } } return; }num[k] = 1; dfs(k+1); num[k] = 0; dfs(k+1); }int main(){ dfs(1); printf("%lld",ans); return 0; } //1046810092

二进制枚举方法
#include using namespace std; /* 二进制枚举 1<<30种 也就是2^30可能情况 对于每个情况 枚举每一位 判断是否满足条件 */int ans = 0; int main(){ for(int i=0; i<(1<<30); i++){ int x = i; int a = 0 ,b = 0, c = 0; bool check = false; while(x){ c = b; b = a; a = x & 1; if(a && !b && c){ check = true; break; } x >>= 1; } if(check) ans++; } cout<

游戏 ok
2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

#include using namespace std; typedef long long LL; LL a = 482333897982347239LL; LL b = 557432748293424892LL; LL k = 1389472389742429877LL; //同余 (b-a) % mod = b*2 % mod void test(){ LL mod = a + b; cout<<(b-a) % mod<>= 1; } return ans; }//快速幂 LL mpow(LL x,LL y,LL p){ LL ans = 1LL; while(y){ if(y & 1LL) ans = mmul(ans,x,p); x = mmul(x,x,p); y >>= 1; } return ans; }int main(){ LL mod = a + b; //首先 c + d = 2*a + b - a=a + b = mod LL ans = mmul(a,mpow(2LL,k,mod),mod); //d = b - a = b - (mod - b) = 2*b - mod = 2*b %mod,所以 b-a 也就等价于 b*2,等价后a与b交不交换都无所谓(因为后面的操作都是*2) printf("%lld\n",min(ans,mod-ans)); //结果确保A更小,A可能是ans 也可能是mod-ans(b) return 0; } //383513242709218605

公约数 ok
蒜头君有n个数,他想要从中选出k个数,使得它们的最大公约数最大。
请你求出这个最大的最大公约数。
输入格式
第一行输入两个整数 。
第二行输入 个整数 。
输出格式
输出一个整数。
数据范围
2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

样例输入1
4 3
2 4 8 3
样例输出1
2
样例输入2
4 2
4 8 6 6
样例输出1
6
思路:
30% 暴力dfs 或者 状态压缩(二进制枚举)选出k个数
另外30% 由概率,直接输出 1。
100% 枚举可能的gcd 值,检查有多少元素被它整除即可。时间复杂度O(nlogn)
30%代码-dfs搜索
#include using namespace std; typedef long long ll; ll ans = 1; ll gcd(ll a,ll b){ if(b==0) return a; return gcd(b,a%b); }const int maxn = 1e6+10; int n,k; int a[maxn]; /* dfs暴搜过30%数据 *//* 不确定的dp状态转移方程:dp[i][k] = max(dp[i][k],gcd(dp[j n+1) return; if(have > k+1) return; if(have == 1){ dfs(cur+1,have,_gcd); dfs(cur+1,have+1,a[cur]); }else{ dfs(cur+1,have,_gcd); dfs(cur+1,have+1,gcd(a[cur],_gcd)); }}int main(){ scanf("%d%d",&n,&k); for(int i=1; i<=n; i++) scanf("%d",&a[i]); dfs(1,1,0); printf("%lld\n",ans); return 0; }

100%代码
#include using namespace std; typedef long long ll; const int N = 1e6 + 7; const int mod = 1e9 + 7; int a[N+10]; int main(){ int n,k,x; memset(a,0,sizeof(a)); scanf("%d%d",&n,&k); for(int i=0; i= k) ans = i; //比k大 那么选出k个数 他们的gcd就是当前的i } printf("%d\n",ans); return 0; }

蒜头图 ok
2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

实际上就是问图里有多少个环,计环的个数为 k,则结果为2^k-1。
30% 状态压缩选出边,判断所选的边是否构成环
100% 并查集统计环的数量,使用并查集每次询问只需要判断这两点之前是否连通就可以了,为什么结果是2^k-1呢(k表示环的数量),有大佬说了:题目表明了“只要构成蒜头图就算一种情况”,那么也就是说从原图中取1个环也能构成环,取其中两个环也能构成环(有环就代表是蒜头图的子图,管你取几个环都是蒜头图).......C(n,i) i从1~k都能构成环 那么C(n,1) + C(n,2) + C(n,3) + C(n,k)就是答案,这个式子的值也等于2^k-1
100%代码-并查集统计环的数量
#include using namespace std; const int mod = 1046513837; int n,m,f[200500]; int find(int x){ return f[x] == x ? x : f[x] = find(f[x]); }void merge(int a,int b){ int x = find(a); int y = find(b); if(x != y ) f[x] = y; }int main(){ scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) f[i] = i; long long ans = 1; for(int i=1 ; i<=m; i++){ int a,b; scanf("%d%d",&a,&b); int x = find(a), y = find(b); if(x == y){//这里如果 两个点祖先一样 说明找到环了 ans <<= 1; if(ans > mod) ans -= mod; }else merge(a,b); printf("%lld\n",ans-1); } return 0; }

区间并 60% 线段树做法不会
2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

2019 蓝桥杯国赛 B 组模拟赛 题解
文章图片

30% 按照题意暴力计算,枚举权值区间,枚举给定的数组的区间 ok
60% 枚举所有的答案区间l~r,看看哪些区间能恰好分成两个不相交的区间,暴力做的话:枚举l和r共O(n^2),加上判断是否恰好分成了两个区间共O(n^3)。判断是否分成了两个区间可以用并查集来维护,查询复杂度可以降到O(1)。
具体怎么用并查集维护?没写代码,但我是这样想的,在原序列中 如果 a[i] 和 a[i-1] 是连续的 即(后面这个数比前面这个数值大1) 就把当前第i个数 加入第i-1的集合中,代表他们是一个区间的。后面查询是否分成两个区间时,只需查询l~r区间内的数被分成了几个集合就可以了。
这个思路我自认为没有问题emmmm
100% 线段树维护区间最大值、次大值、相应个数 O(nlogn) 不会
30%代码-暴力枚举:
#include using namespace std; const int maxn = 3*1e6+5; int n; int m[maxn]; int vis[maxn]; int ans = 0; /* 枚举l~r区间 枚举i~j区间 */int main(){ scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%d",&m[i]); for(int l=1; l<=n; l++){ for(int r=l+1; r<=n; r++){ bool flag = false; for(int a=1; a<=n; a++){ for(int b=a; b<=n; b++){ for(int c=b+1; c<=n; c++){ for(int d=c; d<=n; d++){ bool flag2 = true; for(int i=1; i<=n; i++) vis[i] = 0; for(int i=a; i<=b; i++) vis[m[i]] = 1; for(int i=c; i<=d; i++) vis[m[i]] = 1; for(int i=l; i<=r; i++){ if(vis[i] == 0){ flag2 = false; break; } } if(flag2 && r-l+1 == b-a+1 + d-c+1){ ans++; flag = true; //cout<<"l = "<=1; i--) { x=nt[i]; if (p[x+1]i) work(1,1,n,i,p[x-1]-1,1); if (p[x+1]>i && p[x-1]i && p[x-1]>i) { k1=p[x+1]; k2=p[x-1]; if (k1>k2) swap(k1,k2); work(1,1,n,i,k1-1,1); work(1,1,n,k2,n,-1); } upans(1,1,n,i,n); } printf("%lld",ans-n); return 0; }

【2019 蓝桥杯国赛 B 组模拟赛 题解】转载于:https://www.cnblogs.com/fisherss/p/10857705.html

    推荐阅读