A. Perfectly Imperfect Array
题意
对于每一个测试样例,问是不是存在一个子序列使得他们的乘积不是完全平方数。
思路
只要有一个数不是完全平方数,直接选它当子序列就行了
AC代码
#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 2e3 + 5;
const int N=1005;
using namespace std;
int main() {
int ncase;
cin >> ncase;
while(ncase--){
ll n;
cin >> n;
ll f = 1, num;
for(int i = 1; i <= n; i++){
cin >> num;
if(sqrt(num) * sqrt(num) != num) f = 0;
}
if(f) cout << "NO" << endl;
else cout << "YES" << endl;
}
return 0;
}
B. AND 0, Sum Big
题意
对于每一个测试样例,在 0 到 2k -1 个数中选择 n 个整数(可以重复选),并满足:
- 这 n 个数的 & 运算结果为 0
- 这 n 个数的和最大
求出这样的方案数有多少种,结果对 109+7 取模。
思路
n 个数的和最大,肯定要多选 2k -1。
对于这 n 个数,在二进制位下,有 k 位。
最后的 & 运算结果为 0,每一位上一定存在一个数在这个二进制下为 0。
每一个二进制位下有 k 种选择,故而结果为:nk % (109+7)
AC代码
#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 2e3 + 5;
const int N=1005;
using namespace std;
int main() {
int ncase;
cin >> ncase;
while(ncase--){
ll n, k;
cin >> n >> k;
ll res = 1;
for(int i = 1; i <= k; i++) res = res * n % mod;
cout << res << endl;
}
return 0;
}
C. Product 1 Modulo N
题意
在 1 到 n-1 中,选择最长的一个子序列,这个子序列所有数的乘积对 n 取模的结果为 1。
思路
这里我们先明确一个事情:
令 a % b = 1,一定有 gcd(a, b) = gcd(k*b+1, b) = 1,那么 a 和 b 一定互质。
由上可得,我们选择的数 x 必须满足 gcd(x, n) = 1,否则任何数与 x 的乘积 z 都不满足 gcd(z, n) = 1。
所以,我们肯定是选上所有与 n 互质的数。
令这个序列的乘积结果 x = k*n +p。
显然,p = 1 时,直接输出这个子序列即可。
当 p != 1时,输出这个子序列中除 p 以外的所有数即可。
先证明正确性:x / p = k / p * n + 1,嗯,很明显了,gcd(x / n, n) = 1。
再来证明 p 一定会被选上,即 p 与 n 互质。
由 x = k*n +p,得 p = x % n。
gcd(x, n) = gcd(x%n, n) = gcd(p, n) = 1。
完。
AC代码
#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 3e5 + 5;
const int N=1005;
using namespace std;
int main() {
ll n;
cin >> n;
ll cnt = 0, prod = 1;
for(ll i = 1; i < n; i++){
if(__gcd(i, n) == 1){
cnt++;
prod *= i;
prod %= n;
}
}
if(prod == 1) cout << cnt << endl;
else cout << cnt-1 << endl;
for(ll i = 1; i <= n; i++){
if(__gcd(i, n) == 1){
if(prod != 1 && i == prod) continue;
cout << i << " ";
}
}
cout << endl;
return 0;
}
D. Cut and Stick
题意
给出一个有 n 个元素的数组,有 m 次查询。每次查询,给出一个区间(l, r),问这个区间最少可以划分为多少个子序列,每个子序列中的众数都不超过区间长度(向上取整)的一半。
思路
莫队处理区间众数(模板可以直接copy)。
特别注意向上取整这个点,下边要用这个卡bug。
如果整个区间的众数小于区间长度的一半则不用在额外划分。
如果整个区间的众数大于区间长度的一半:
令区间的长度为 cnt,区间的众数为 x,则区间剩下的数有 cnt - x 个数。
我们把两个众数和一个非众数分到一块,显然,这种方式可以划走更多的众数。
这时一共划分了 cnt - x 个子序列,还剩下cnt - 3*(cnt - x) 个众数。
把剩下的众数都单独划分为一个子序列,这样划分的子序列总数为:cnt - x + cnt - 3 * (cnt - x) = cnt - 2 * (cnt - x) = 2 * x - cnt
感谢林总提供的最优性证明
AC代码
#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 3e5 + 5;
const int N=1005;
using namespace std;
int a[maxn], ANS = 0, tm[maxn], cnt[maxn], ans[maxn];
typedef struct Node{
int l, r;
int id;
} node;
int sqt;
bool cmp(node A, node B){
if(A.l/sqt == B.l/sqt) return A.l/sqt%2 == 1 ? A.r < B.r : A.r > B.r;
else return A.l/sqt < B.l/sqt;
}
node q[maxn];
void add(int num){
--cnt[tm[num]];
ANS = max(ANS, ++tm[num]);
++cnt[tm[num]];
}
void subd(int num){
--cnt[tm[num]];
if(ANS == tm[num] && cnt[tm[num]] == 0) ANS--;
--tm[num], ++cnt[tm[num]];
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
sqt = sqrt(n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= m; i++) scanf("%d %d", &q[i].l, &q[i].r);
for(int i = 1; i <= m; i++) q[i].id = i;
sort(q+1, q+1+m, cmp);
for(int l = 1, r = 0, i = 1; i <= m; i++){
int ln = q[i].l, rn = q[i].r;
while(l < ln) subd(a[l++]);
while(l > ln) add(a[--l]);
while(r < rn) add(a[++r]);
while(r > rn) subd(a[r--]);
ans[q[i].id] = 1;
if(ANS > (rn-ln+2)/2) ans[q[i].id] = 2 * ANS - (rn-ln+1);
}
for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
return 0;
}