A
题意
输出 1378n 1378 n 的个位数字是多少?
分析
我们知道对于一个数的n次幂,他的个位数字会以一种循环节的形式呈现。原因是个位数字只有十个,当十个用完的时候,回到最初的两个数相乘,一定会出现一个循环节。这次我比较懒,直接手写出了 8n 8 n 的个位循环节情况为 8,4,2,6 8 , 4 , 2 , 6 ,然后根据n模4的取余情况确定个位数字即可。
代码
#include <bits/stdc++.h>
using namespace std;
int a[] = {8, 4, 2, 6};
int main(){
int n;
scanf("%d", &n);
if(!n) puts("1");
else printf("%d\n", a[(n-1)%4]);
return 0;
}
B
题意
给出 n n 个数,求这个数中,两个数异或后,结果为 x x 的组合种数有多少?即满足 (i<j) ( i < j ) 的个数。
分析
对于异或操作我们需要知道:
x⊕x=0
x
⊕
x
=
0
,若
x⊕y=z
x
⊕
y
=
z
, 则
x⊕z=y
x
⊕
z
=
y
那么题目就转化为了给出任意一个数
ai
a
i
,查询
ai⊕x
a
i
⊕
x
的个数,或者
ai
a
i
与
ai⊕x
a
i
⊕
x
两两组合的组合数有多少种 。
考虑到题目的数据范围很小,只有
1e5
1
e
5
,所以我们可以直接对每个数的出现情况计数,然后直接组合求解。需要注意的是,由于组合的下标不能相等,所有如果出现
x=0
x
=
0
的情况,需要对组合进行额外的判断。如果数据的取值范围增大,我们也可以用
set
s
e
t
来完成操作。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int a[maxn], cnt[maxn];
int main(){
int n, k;
ll ans = 0;
memset(cnt, 0, sizeof cnt);
scanf("%d%d", &n, &k);
for(int i=0; i<n; i++){
scanf("%d", &a[i]);
cnt[a[i]]++;
}
for(int i=0; i<maxn; i++){
if(cnt[i] && (i^k)<maxn && cnt[i^k]){
if(k==0){
ans += (ll)cnt[i] * (ll)(cnt[i]-1) / 2;
}
else {
ans += (ll)cnt[i] * (ll)cnt[i^k];
cnt[i] = cnt[i^k] = 0;
}
}
}
printf("%I64d\n", ans);
return 0;
}
C
题意
每个人都有且只有一个自己喜欢的人,他们在玩一个游戏,问能不能找到一个数 k k , 使得从任意一个人开始,沿着他们喜欢的人的链往后找 k k 个人可以找到,游戏重新从y开始,再往后找 k k 个人可以找到找出最小的 k k ,如果不能,组输出
分析
首先我们进行模型的转化,我们认为人是独立的点,他们之间喜欢的关系为一条有向边,以此来建图。难么游戏的本质就是,从一个点出发,沿着唯一的一条有向路径(所有节点的出度为
1
1
),经过的长度后回到了自己。
而回到自己的概念是什么?图上存在有向环。而因为需要在图上落脚,即
k2
k
2
应当是有意义的。所以当环的长度为偶数时,我们取环长的一半,为奇数时我们取环长,然后取所有环长的最小公倍数
(lcm)
(
l
c
m
)
。即为最小的k。
什么时候无解呢?就是环上有点的入度大于1, 即有点不能回到自己的时候。我选择了用dfs判环并记录路径长度。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
int a[maxn], ans;
bool vis[maxn], can;
int gcd(int x, int y){
return y==0?x:gcd(y, x%y);
}
void dfs(int x, int s, int len){
//cout<<x<<" "<<s<<" "<<len<<" "<<ans<<endl;
if(vis[x]){
if(x==s) {
if(len&1)
ans = ans*len/gcd(ans, len);
else {
len /= 2;
ans = ans*len/gcd(ans, len);
}
}
else can = false;
}
else {
vis[x] = true;
dfs(a[x], s, len+1);
}
}
int main()
{
int n;
memset(vis, false, sizeof vis);
ans = 1;
can = true;
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
}
for(int i=1; i<=n; i++){
if(!vis[i]){
dfs(i, i, 0);
}
}
if(can) printf("%d\n", ans);
else printf("-1\n");
}
D
题意
有 n n 个漂亮女孩,每个人有一个魅力值和体重,有一个舞台,舞台的承重最大为。现在知道女孩儿们有朋友圈,朋友圈里的朋友,和自己也是一个朋友圈的,有m条朋友之间的关系。对于每一个朋友圈而我们只能请朋友圈中的所有人,或者朋友圈中的一个人,当然也可以不请。问,在保证舞台可以承担的条件下,最大可以获得魅力值是多少。
分析
朋友圈中的朋友 的朋友,和自己是一个朋圈的,这个概念我们可以很自然地联想到dfs求联通块,或者并查集求分组。
而对于在限定空间的条件下,取更多的价值,我们自然而言的想到了背包问题。只是这里不同的是,由于分组的不同我们所选用的策略也是不同的,那么我们用到的就是分组背包的思想(这里不做详细说明)。
需要注意的一点是,由于没有说朋友圈内不会再给出朋友关系,所以需要特殊判断一下。emmmm,本来是个小操作,不过因为我自己忽视了这点结果wa了两发就很难受了。(并查集+分组背包)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1005;
struct Node{
int fa;
int wg;
int be;
int sumw;
int sumb;
}a[maxn];
int dp[maxn][maxn], vis[maxn];
int found(int x) {
int p = x;
while(a[p].fa!=p)
p = a[p].fa;
while(x!=p) {
int temp = a[x].fa;
a[x].fa = p;
x = temp;
}
return p;
}
void mex(int x, int y){
int fax = found(x), fay = found(y);
if(fax==fay) return;
a[fax].fa = fay; // fax->fay
a[fay].sumb += a[fax].sumb;
a[fay].sumw += a[fax].sumw;
}
int main()
{
int n, m, w;
memset(vis, false, sizeof vis);
scanf("%d%d%d", &n, &m, &w);
for(int i=1; i<=n; i++) {
scanf("%d", &a[i].wg);
a[i].sumw = a[i].wg;
a[i].fa = i;
}
for(int i=1; i<=n; i++){
scanf("%d", &a[i].be);
a[i].sumb = a[i].be;
}
for(int i=1; i<=m; i++){
int u, v;
scanf("%d%d", &u, &v);
mex(u, v);
}
dp[0][0] = 0;
int cc = 1;
for(int j=1; j<=n; j++){
int now = found(j);
if(vis[now]) continue;
vis[now] = true;
for(int i=1; i<=w; i++){
dp[cc][i] = dp[cc-1][i];
if(a[now].sumw <= i)
dp[cc][i] = max(dp[cc][i], dp[cc-1][i-a[now].sumw]+a[now].sumb);
}
for(int t=1; t<=n; t++){
if(found(t)!=now) continue;
for(int i=1; i<=w; i++){
if(a[t].wg <= i){
dp[cc][i] = max(dp[cc][i], dp[cc-1][i-a[t].wg]+a[t].be);
}
}
}
cc++;
}
int ans = 0;
for(int i=w; i>=0; i--){
ans = max(dp[cc-1][i], ans);
}
printf("%d\n", ans);
return 0;
}
E
题意
已知有
n
n
个机场,编号~
n
n
,机场有两种类型:和
1
1
,机场飞往机场
j
j
,类型相同那么花费为
0
0
类型不同则花费。
现给出机场
a
a
,求
a
a
到的最小花费.
分析
两种情况:
1. 若
a
a
, 类型相等,那么最小花费为
0
0
2. 若,
b
b
类型不等,那么最小花费为 (因为相同类型机场花费为
0
0
,总会存在一对不同类型机场相邻的情况:这样子,从相邻的机场跳过去就可回到第一种情况。花费只有在改变机场类型时候产生
1
1
的花费。
代码
#include <cstdio>
#include <cstring>
const int maxn=1e5+10;
char w[maxn];
int main()
{
int n,a,b;
scanf("%d%d%d%s",&n,&a,&b,w+1);
if(w[a]==w[b]) puts("0");
else puts("1");
}
F
题意
定义操作:
一个数列初始值是
1
1
,进行如下操作来形式成一个新数列:“
mex(s)
m
e
x
(
s
)
s
s
”
为数列
s
s
中没有出现的最小整数。
即
step2:1 2 1
s
t
e
p
2
:
1
2
1
(
2
2
为))
step3:1 2 1 3 1 2 1
s
t
e
p
3
:
1
2
1
3
1
2
1
(
3
3
为))
step4:1 2 1 3 1 2 1 4 1 2 1 3 1 2 1
s
t
e
p
4
:
1
2
1
3
1
2
1
4
1
2
1
3
1
2
1
(
4
4
为))
现给出询问,回答第
n
n
次操作后形成的序列的第个数字是什么。
分析
首先,显然该数列是递归定义的。我们也可以递归的求解。
因为这个序列是每次倍增的,所以对应位置的数也是指数式的。因此我们可以寻找到k的固定位置为
2n−1
2
n
−
1
值固定为
n
n
。
又因为数列是对称的,那么如果k在前一半,直接递归,如果是在后一半,那么先减一半(对称过去)再递归。
代码
#include <iostream>
typedef long long ll;
using namespace std;
int meo(ll n, ll k){
if (k == (1ll<<n-1)) return n;
return solve(n-1, k<(1ll<<n-1)?k:k-(1ll<<n-1));
}
int main(){
ll n, k;
cin >> n >> k;
cout << meo(n,k) << endl;
return 0;
}
G
题意
已知,求满足 2n=1x+1y+1z的不相等的x,y,z 2 n = 1 x + 1 y + 1 z 的 不 相 等 的 x , y , z
分析
此题为构造题。观察式子本身容易得知
2n=1n+1n
2
n
=
1
n
+
1
n
。
不妨设
x=n
x
=
n
那么只需要求出
1n=1y+1z
1
n
=
1
y
+
1
z
的解。此时我们可以设
y
y
为一个数如: 那么
1n−1n+1=1(n+1)∗n
1
n
−
1
n
+
1
=
1
(
n
+
1
)
∗
n
即
z=n∗(n+1)
z
=
n
∗
(
n
+
1
)
此题可有多组解。解法类似。
代码
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
if (n == 1)
cout<<-1<<endl;
else
cout<<n<<" "<<n + 1<< " "<<n*(n+1)<< endl
}
H
题意
给出一颗树,我们假定树的根为1。现在需要找出两颗互相无交叉的子树,使得他们的权值和最大。
分析
一开始看到这个题时特别的莽,两颗子树的话我只要保证我跟下有两颗以上的子树,然后从中取权值最大的两个,所得到的就是最优的。结果发现原来树上节点的权值可以为负的,就很头疼了。
但是由于树是递归的,所以我们在求解树上的问题时,也可以用递归的方式求解。当前子树我们可以求出一棵最大的子树和当前子树的最大权值,即对于以
u
u
为根节点的子树,分为包含u的子树权值之和以及不包含的,
u
u
的一棵最大权值的子树和。分别维护后更新。可以理解为,是通过dfs递归求解的方式,在树上进行更新操作,最后求得最优解
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long LL;
const LL INF = 1LL<<60;
struct node
{
int v, nxt;
}edge[maxn<<1];
LL dp[maxn], sum[maxn], w[maxn], head[maxn], tot, ans;
void add(int u, int v) {
edge[tot].v = v; edge[tot].nxt = head[u]; head[u] = tot++;
}
void dfs(int u, int fa) {
sum[u] = w[u];
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(v == fa) continue;
dfs(v, u);
sum[u] += sum[v];
if(dp[u] > -INF) ans = max(ans, dp[u] + dp[v]);
dp[u] = max(dp[u], dp[v]);
}
dp[u] = max(dp[u], sum[u]);
}
int main()
{
int n;
scanf("%d", &n);
memset(head, -1, sizeof(head));
for(int i = 1; i <= n; i++)
scanf("%I64d", &w[i]);
for(int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v); add(v, u);
}
ans = -INF;
for(int i = 1; i <= n; i++) dp[i] = -INF;
dfs(1, -1);
if(ans <= -INF) puts("Impossible");
else printf("%I64d\n", ans);
return 0;
}