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;
}