A. Johnny and Ancient Computer(因子拆分)
分析
- 题意
- 给我们两个数a、b,现在我们我们可以对 a来了进行一些系列的x2 、x4、 x8、 /2 、/4、 /8 等运算操作,问我们通过一些这样的操作以后能否把a变成b,如果能输出需要的最小次数,否则输出-1
- 分析
- 这个题中既可能有乘法 又有 除法操作,我们可能 在输入a、b的之后,如果 a < b的话,我们交换二者的值,令a始终保持较大值,这样我们只需要对a进行 除法操作了,如果 对a进行 /2 /4 /8的操作之后要想得到最终结果 a == b ,那我们可以令c = a / b(这里要确保 a % b == 0),这样我们依次从c中拆分出尽可能多的 8、在拆分尽可能多的4、最后拆分出尽可能对2 ,这样拆分完之后如果剩下的数不是1的话,直接输出-1,否则的话答案就是拆分出 8,4,2的总共数量
代码
#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod 998244353
int br[10];
int ar[5];
int main()
{
/* fre(); */
int T;
scanf("%d\n", &T);
ar[1] = 8, ar[2] = 4, ar[3] = 2;
while(T --)
{
ll a, b;
scanf("%lld %lld", &a, &b);
if(a < b) swap(a, b);
if(a % b)
{
printf("-1\n");
continue;
}
ll c = a / b;
memset(br, 0, sizeof(br));
for(int i = 1; i <= 3; i ++)
{
while(c % ar[i] == 0)
{
c /= ar[i];
br[ar[i]] ++;
}
}
if(c != 1)
{
printf("-1\n");
continue;
}
printf("%d\n", br[2] + br[4] + br[8]);
}
return 0;
}
B. Johnny and His Hobbies(暴力)
分析
- 题意
- 给我们s个数让我们照出一个最小正整数k,是s个数都 异或k 得到的值形成一个集合a,要求a与s中所包含的元素完全相同,求这个k
- 分析
- 由于数据范围比较小,我们直接暴力求解k的最合适值
代码
#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod 998244353
const int mxn = 1e3 + 100;
int ar[mxn];
int br[mxn];
int cr[mxn];
int main()
{
/* fre(); */
int T;
scanf("%d\n", &T);
while(T --)
{
memset(br, 0, sizeof(br));
int n;
scanf("%d\n", &n);
for(int i = 1; i <=n; i ++)
scanf("%d", &ar[i]), br[ar[i]] = 1;
int ans = 0;
for(int i = 1; i <= 1024; i ++)
{
memset(cr, 0, sizeof(cr));
int fg = 1;
for(int j = 1; j <= n; j ++)
{
int t = (ar[j] ^ i);
if(++ cr[t] >= 2)
{
fg = 0;
break;
}
}
if(fg == 0)
continue;
for(int j = 0; j <= 1024; j ++)
{
if(br[j] != cr[j])
{
fg = 0;
break;
}
}
if(fg)
{
ans = i;
break;
}
}
if(ans == 0)
printf("-1\n");
else
printf("%d\n", ans);
}
return 0;
}
C. Johnny and Another Rating Drop
分析
- 分析
000
001
010
011
100
101
- 因为二进制是逢二进一,所以我们依次观察二进制的最地位到高位发现,
- 对于 二进制值倒数第1位,这一位对答案的贡献是 n
- 对与 二进制的倒数第2位,这一位对答案的贡献是 n / 2
- 对于 二进制的倒数第3位,这一位对答案的贡献是 n / 4
- 综上 二进制的倒数第i位,对答案的贡献是 n / 2 i − 1 n/2^{i-1} n/2i−1,
- 所以 答案 ans = n/1 + n/2 + n/4 …
- 还有一种方法也是规律,规律是:2*n - 二进制中1的数量 =ans,,,这个规律为什么对,我也无法解释,但 确实ac了
代码一
#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod 998244353
const int mxn = 1e6 + 100;
int main()
{
/* fre(); */
int T;
scanf("%d", &T);
while(T --)
{
ll n, i = 1, ans = 0;
scanf("%lld\n", &n);
ll now = n;
while(now)
{
ans += n / i;
i *= 2;
now >>= 1;
}
printf("%lld\n", ans);
}
return 0;
}
代码二
#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod 998244353
const int mxn = 1e3 + 100;
int main()
{
/* fre(); */
int T;
scanf("%d\n", &T);
while(T --)
{
ll n, t;
scanf("%lld", &n);
t = n;
ll ct = 0;
while(n)
{
if(n & 1) ct ++;
n >>= 1;
}
printf("%lld\n", t * 2 - ct);
}
return 0;
}
D. Johnny and Contribution(模拟+贪心)
分析
- 题意
- 给我们一个有n个节点m边的无向图,每个节点有一个 1~n的目标权值,然后我们对每个节点进行染色(给这个节点赋上一个 1~n的权值),对于某个节点我要染色的规则是,与当前节点相邻的节点中没有出现过的颜色(权值数字)中最小的那个权值数字我赋值给当前的 节点,通过这样的染色操作之后,如果每个节点的新权值与 目标权值都先相同的话,输出染色节点的顺序,否则输出-1
- 分析
- 贪心,易得我们应该按照期望从小到大涂色,首先我们包期望为1的节点u都涂掉,然后判断与其直接相连的节点v 如果如果它的期望也为1,我们就把v的期望颜色++,
- 以此类推,因为是从小开始涂颜色的,所以保证比当前u颜色小的节点都涂过颜色了,接着我就直接判断,只要当前节点u的期望不是原来的颜色,我们就可以直接返回-1,最后 注意一下初始化期望颜色为1
代码
#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod 998244353
const int mxn = 1e6 + 100;
vector<int> e[mxn];
vector<int> col[mxn];
vector<int> ans;
int num[mxn];
int main()
{
/* fre(); */
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1, u, v; i <= m; i ++)
{
scanf("%d %d", &u, &v);
e[u].pb(v);
e[v].pb(u);
}
for(int i = 1, t; i <= n; i ++)
{
scanf("%d", &t);
col[t].pb(i);
num[i] = 1; //初始的时候,每个节点周围只有一个 自己1个节点
}
for(int i = 1; i <= n; i ++)
{
for(auto u : col[i])
{
if(num[u] != i) printf("-1\n"), exit(0);
for(auto v : e[u])
if(num[v] == i) num[v] ++;
ans.pb(u);
}
}
for(auto i : ans) printf("%d ", i);
return 0;
}