Codeforces Round #716 (Div. 2) 题解
比赛链接
比赛时只出两题,B题卡了半天。C题因为在自习室打的比赛,电脑没电了没交上,吐了。D题没学过,赛后补了一下。
A题
题意:给定一个序列,要求确定其子序列的乘积是否能不为完全平方数。
只需要遍历一遍整个序列,如果有个数不是完全平方数,那么就一定存在一个乘积不为完全平方数的子序列。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 100010;
int n, m;
int g[N];
int main()
{
int T;
cin >> T;
while (T -- )
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> g[i];
bool flag = true;
for (int i = 1; i <= n; i ++ )
{
int l = 1, r = g[i];
while (l < r)
{
int mid = l + r >> 1;
if (mid * mid >= g[i]) r = mid;
else l = mid + 1;
}
if (l * l != g[i])
{
flag = false;
break;
}
}
if (flag) puts("NO");
else puts("YES");
}
return 0;
}
B题
题意:给定n和k,要求找出在0 ~ 2k - 1之间的序列长度为n的且总的a1 & a2 & ...... & an = 0。
我们将每个数的二进制展开之后发现,如果要序列&的结果为0就必须在每一位上有一个0,那么总共有k个位置,每个位置0都有n中选择,所以结果就是nk。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int n, m;
int g[N];
int s[25];
int main()
{
int T;
cin >> T;
while (T -- )
{
cin >> n >> m;
if (n == 1) cout << 1 << endl;
else
{
LL res = 1;
for (int i = 1; i <= m; i ++ )
res = res * n % mod;
cout << res << endl;
}
}
return 0;
}
C题
题意:给定一个数字n,要求找出从1 ~ n-1之间的最长的序列使得序列的乘积%n的结果为1。
题意很简单,但是很毒瘤。
证明:
我们的结果必定是满足%n的结果为1那么就必然有gcd(n * m + 1, n) = 1,则跟我们可以得到n * m + 1与n时互质的,那么如果我们选择了一个不与n互质的数,我们的结果就必然和n有公约数,就不能满足是互质的,所以我们只能选择与n互质的数。
同时由于a和b都与n互质,即gcd(a, n) = 1, gcd(b, n) = 1,所以我们必然就有gcd(a * b, n) = 1,所以我们把所有的互质的数都选上得到的结果必然是与n互质的数。
之后我们就可以判断res % n 是否大于1,若等于1那这个就是结果,不等于1时由于我们满足gcd(res, n) = 1,则必然满足gcd(n, res % n) = 1,所以res % n的结果与n互质,这样我们就可以知道res % n必然是等于我们所选择的数字中的其中一个,将其找到之后删掉就好了。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int g[N];
int n, cnt;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
cin >> n;
if (n == 2)
{
cout << 1 << endl << 1 << endl;
return 0;
}
LL res = 1;
for (int i = 1; i <= n; i ++ )
if (gcd(i, n) == 1)
res = res * i % n, g[++ cnt] = i;
if (res % n == (n - 1))
cnt -- ;
cout << cnt << endl;
for (int i = 1; i <= cnt; i ++ )
cout << g[i] << ' ';
cout << endl;
return 0;
}
D题
题意:给定一个长度为n的序列,和m个询问,每次询问一个区间(l, r),要求我们求出在这区间内的数最小需要分成多少个子序列才能使得每个子序列中的数出现的次数小于区间长度除二上取整。
莫队算法,可以说是莫队算法的经典题,比赛时没学过,之后看了一下发现这东西确实很优雅,不愧为优雅的暴力算法。
以下证明为借鉴别人的证明,附上链接。
原文地址
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 400100, mod = 1e9 + 7;
int res[N];
int n, m;
struct query
{
int l, r, id, bl;
}q[N];
int g[N];
int num[N];
int cnt[N];
int maxv;
inline int read()
{
int x = 0;char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x;
}
inline bool cmp(query a, query b)
{
if (a.bl != b.bl) return a.l < b.l;
if (a.bl & 1) return a.r < b.r;
else return a.r > b.r;
}
inline void add(int x)
{
num[cnt[g[x]]] -- ;
cnt[g[x]] ++ ;
num[cnt[g[x]]] ++ ;
maxv = max(maxv, cnt[g[x]]);
}
inline void del(int x)
{
num[cnt[g[x]]] -- ;
if (maxv == cnt[g[x]] && num[cnt[g[x]]] == 0) maxv -- ;
cnt[g[x]] -- ;
num[cnt[g[x]]] ++ ;
}
int main()
{
n = read(), m = read();
int lenth = sqrt(n);
for (int i = 1; i <= n; i ++ )
g[i] = read();
for (int i = 1; i <= m; i ++ )
{
int l, r;
l = read(), r = read();
q[i] = {l, r, i, (l - 1) / lenth + 1};
}
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i ++ )
{
int ql = q[i].l, qr = q[i].r;
int len = qr - ql + 1;
while (l < ql) del(l ++ );
while (l > ql) add( -- l);
while (r < qr) add( ++ r);
while (r > qr) del(r -- );
res[q[i].id] = max(1, 2 * maxv - len);
}
for (int i = 1; i <= m; i ++ )
cout << res[i] << endl;
return 0;
}