Codeforces Round #747 (Div. 2)
A. Consecutive Sum Riddle
思路分析:
- 一开始想起了那个公式
l
+
(
l
+
1
)
+
…
+
(
r
−
1
)
+
r
=
(
l
+
r
)
(
r
−
l
+
1
)
/
2
l + (l + 1) + … + (r − 1) + r = (l + r)(r - l + 1) / 2
l+(l+1)+…+(r−1)+r=(l+r)(r−l+1)/2。
- 然后一看令
l
+
r
=
1
l + r = 1
l+r=1最合适,那么就有
l
=
r
−
1
l = r - 1
l=r−1,一代入就得到
r
=
n
,
l
=
−
n
+
1
r = n, l = -n + 1
r=n,l=−n+1。
- 没想通为什么没有一眼看出来。
代码
#include
#define ll long long
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
ll n;
cin >> n;
cout << -n + 1 << ' ' << n << endl;
}
return 0;
}
B. Special Numbers
思路分析
- 这题也是想久了,其实列一下规律一下就出来了(当然不排除大佬一眼看出来)。
- 我们列一下前几项吧。
-
k
=
1
,
2
,
3
,
4
,
5
k = 1,2,3,4,5
k=1,2,3,4,5,我们分别选的是
n
0
n ^ 0
n0,
n
1
n ^ 1
n1,
n
0
+
n
1
n ^ 0 + n ^ 1
n0+n1,
n
2
n ^ 2
n2,
n
0
+
n
2
n ^ 0 + n ^ 2
n0+n2。
- 然后我们就可以得出一个规律,那就是我们把
k
k
k变成二进制,如果当前二进制位为
1
1
1的话我们就加上
n
x
n ^ x
nx,
x
x
x是指该二进制位是第几位,然后注意longlong 和 取模即可。
代码
#include
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
ll n, k;
cin >> n >> k;
ll ans = 0;
ll p = 1;
for (int j = 0; j <= 31; j++)
{
if (k & (1 << j))
{
ans = (ans + p) % mod;
}
p *= n;
p %= mod;
}
cout << ans << endl;
}
return 0;
}
C. Make Them Equal
思路分析
- 这题也挺简单的,很容易想到最多需要两次操作,因为
1
<
=
x
<
=
n
1 <= x <= n
1<=x<=n,所以我们只要选
n
−
1
n - 1
n−1和
n
n
n必然能完成任务,因为选
n
n
n就把除
n
n
n这个位置以外的位置全部弄好了,然后就是
n
−
1
n-1
n−1必然不会被
n
n
n整除。
- 所以我们就要思考一下只要一次操作和0次操作的情况。
- 看下题目要求的时间,试试暴力(乌鱼子,我还想是不是质因数分解然后拿最小的质因数和
n
n
n比大小,不知道有同学这样试了没)。
- 暴力的时候注意一下,
o
(
n
2
)
o(n^2)
o(n2)是过不了这题的,所以我们以
x
x
x为第一层循环,这样能优化时间。因为这样的话我们下标就不用一个一个遍历,只需要加上
x
x
x即可。
代码
#include
#define ll long long
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
vector ans;
bool ok = true;
int n;
cin >> n;
char ch;
cin >> ch;
string s;
cin >> s;
for (int i = 0; i < s.size(); i++)
{
if (s[i] != ch)
{
ok = false;
}
}
if (!ok)
{
for (int i = 1; i <= n; i++)
{
ok = true;
for (int j = i; j <= n; j++)
{
ok &= (s[j - 1] == ch);
j += i - 1;
}
if (ok)
{
ans.push_back(i);
break;
}
}
}
if (!ok)
{
ans.push_back(n);
ans.push_back(n - 1);
}
cout << ans.size() << endl;
for (int x : ans)
{
cout << x << ' ';
}
cout << endl;
}
return 0;
}
D. The Number of Imposters
思路分析
- 我们可以把
i
m
p
o
s
t
e
r
imposter
imposter表示为相反关系,即如果我认为他说的是假话,那么如果我说的是真的,他就是假的,我如果是假的,他就是真的,
c
r
e
w
m
a
t
e
crewmate
crewmate刚好相反。
- 我们考虑用带权并查集解决这个问题,我们维护几个根节点,因为题目所给的点必定能形成几颗树。
- 我们共要维护两个值,一个是与根节点相同关系的节点个数,一个是与根节点相反关系的节点的个数。
- 这样的话答案就是对于每一个根节点,取两种类型中最大的值。
- 那么我们如何来维护这个个数或者说如何构造出这两种节点呢?
- 首先,我们维护一种关系,1表示相反,0表示相同。那么这个点与根节点相同和相反就和中间点的过程有关了。具体看代码。
代码
#include
using namespace std;
const int maxn = 2e5 + 10;
int p[maxn], dis[maxn];
int cnt[maxn][2];
int find(int x)
{
if (x != p[x])
{
int root = find(p[x]);
dis[x] ^= dis[p[x]];
//dis[p[x]],所以其实就是判断x和它的根节点是否关系相同
p[x] = root;
}
return p[x];
//找到父节点并更新dis
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
p[i] = i;
dis[i] = 0;
cnt[i][0] = 1;
cnt[i][1] = 0;
//重置
}
bool flag = 1;
for (int i = 1; i <= m; i++)
{
int u, v;
string s;
cin >> u >> v >> s;
bool val = s[0] == 'i' ? 1 : 0;
//当前两个点的关系
int fu = find(u), fv = find(v);
if (fu == fv)
{
if ((dis[u] ^ dis[v]) != val)
{
flag = 0;
}
//如果两个点已经在同一棵子树了,如果这两个点与根节点的关系异或出来不是输入的关系时矛盾
}
else
{
p[fv] = fu;
dis[fv] = dis[u] ^ dis[v] ^ val;
//把这两个点的父节点连起来,那么父节点的关系应该变成这个个节点异或起来再和当前关系异或即可
cnt[fu][1] += cnt[fv][dis[fv] ^ 1];
//1表示与根节点相反
cnt[fu][0] += cnt[fv][dis[fv]];
//0表示与根节点相同
}
}
if (!flag)
{
cout << -1 << endl;
}
else
{
int ans = 0;
for (int i = 1; i <= n; i++)
{
if (find(i) == i)
{
ans += max(cnt[i][0], cnt[i][1]);
}
}
cout << ans << endl;
}
}
return 0;
}
参考链接:https://blog.csdn.net/weixin_45799835/article/details/120672329?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163376881016780357230502%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163376881016780357230502&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2
allfirst_rank_ecpm_v1~rank_v31_ecpm-1-120672329.first_rank_v2_pc_rank_v29&utm_term=The+Number+of+Imposters&spm=1018.2226.3001.4187
E1. Rubik’s Cube Coloring (easy version)
思路分析
- 这题太水了吧,直接第一个节点能选六个,其他节点只能选四种颜色,所以答案就是
6
×
4
2
k
−
2
6times4^{2^k - 2}
6×42k−2。
代码
#include
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int k;
cin >> k;
ll ans = qpow(4, (1ll << k) - 2) % mod * 6 % mod;
cout << ans << endl;
return 0;
}