5题场, 因为下午有事只能VP一小时,切了ABC溜了,D一看就是个主席树或者莫队,回来再补,E还没看。
A. Perfectly Imperfect Array
一开始读错题交了三发dirt,题面第二行意义不明。
题意:给100个数,如果其中存在一个子序列的乘积不是完全平方数直接输出YES,否则输出NO。
试想如果一个数存在一个幂次为奇数的质因子,那么为了让他变成完全平方数一定要保证其他的数字同时要有奇数次幂的该质因子。
如果存在这种情况,那么数列中任意三个数字的乘积的该质因数项次幂一定是奇数,即非完全平方数。
因此只需要对每一个数字进行质因数分解,存在奇数次幂即输出YES。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int MAXM = 1e4 + 10;
int n;
int a[1000], inv[MAXM];
int main() {
int T; scanf("%d", &T);
while(T--) {
memset(a, 0, sizeof(0));
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
bool flag = false;
for(int i = 1; i <= n; ++i) {
memset(inv, 0, sizeof(inv));
for(int j = 2; j * j <= a[i]; ++j) {
while(a[i] % j == 0) {
inv[j]++;
a[i] /= j;
}
}
if(a[i] > 1) inv[a[i]]++;
for(int i = 1; i <= MAXM - 5; ++i) {
if(inv[i] % 2 != 0) flag = true;
}
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}
B. AND 0, Sum Big
sb题,直接输出n^k即可。
#include <cstdio>
#include <iostream>
#include <cstring>
#define LL long long
using namespace std;
const LL mod = 1e9 + 7;
inline LL quick_pow(LL a, LL b) {
LL base = 1;
for(; b; b >>= 1, a = (a * a % mod) % mod) {
if(b & 1) base = (base * a % mod) % mod;
}
return base % mod;
}
int main() {
int T; scanf("%d", &T);
while(T--) {
LL n, k;
scanf("%lld%lld", &n, &k);
printf("%lld\n", quick_pow(n, k));
}
return 0;
}
C. Product 1 Modulo N
题意:要求你找出(1...n-1)这个排列中的最长子序列,使其满足乘积%n=1。
初等数论的应用,首先我们发现1一定在答案里,其次我们发现如果gcd(a,n)不等于1,并把a加入答案,那么无论怎么组合这个子序列的乘积%n都不可能为1。
之后利用二次剩余的知识我们发现小于n的所有互质数的乘积%n要么等于n-1要么=1,当且仅当n=p^k或n=2*p^k时能取到n-1。
那么我们只需要用埃筛筛完然后把剩下的数乘起来看看mod n是不是=1,不是的话删掉n-1那一项就行。
#include <cstdio>
#include <iostream>
#include <cstring>
#define LL long long
using namespace std;
const int MAXM = 1e5 + 10;
LL inv[MAXM];
bool isP[MAXM];
int main() {
LL n; scanf("%lld", &n);
LL cnt = 0;
LL temp = n;
for(LL i = 2; i * i <= temp; ++i) {
while(temp % i == 0) {
inv[++cnt] = i;
while(temp % i == 0) temp /= i;
}
}
if(temp > 1) inv[++cnt] = temp;
for(LL i = 1; i <= cnt; ++i) {
for(LL j = inv[i]; j <= n - 1; j += inv[i]) {
if(!isP[j]) isP[j] = true;
}
}
LL base = 1, ans = 0;
for(LL i = 1; i <= n - 1; ++i) {
if(!isP[i]) base = base * i % n, ++ans;
}
if(base % n != 1) {
--ans;
printf("%lld\n", ans);
for(LL i = 1; i <= n - 2; ++i) if(!isP[i]) {
printf("%d ", i);
}
}
else {
printf("%lld\n", ans);
for(LL i = 1; i <= n - 1; ++i) if(!isP[i]) {
printf("%d ", i);
}
}
return 0;
}
D. Cut and Stick
题意:长度为n的序列,每次给询问lr,问区间里的数最少分成几组能够保证出现最多的数的出现次数不超过每个组长度一半向上取整。
回来补D,第一眼就是莫队维护区间众数,3e5的范围可以冲。
想了想细节,我们记众数出现次数为x,其余数出现次数为y,假如y>=x+1,那么直接整个区间就满足需求不需要操作直接输出1;
反之,易证划分的一组中众数的个数最多比其余数多一个,那么y个数可以带走x+1个众数,剩下的众数每一个分一组,答案即为x-y。
因此只需要莫队维护区间众数,然后直接输出max(1,x-y)即可,直接找了个板子改一改交了。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=3e5+10;
int N,M,B;
int a[MAXN], pos[MAXN];
int ANS[MAXN];
int b[MAXN];
struct Req{
int L,R,id;
bool operator < (const Req& q1) const{
if(pos[L]!=pos[q1.L]) return pos[L] < pos[q1.L];
else return R < q1.R;
}
} q[MAXN], q2[MAXN];
int ans = 0, cnt[MAXN];
int num[MAXN];
void update(int id, int f){
int x=a[id];
if(f==1){
--num[cnt[x]];
++cnt[x];
++num[cnt[x]];
if(cnt[x]>ans) ans=cnt[x];
}
else{
--num[cnt[x]];
if(ans==cnt[x]&&num[cnt[x]]==0) --ans;
--cnt[x];
++num[cnt[x]];
}
}
int main(){
scanf("%d%d", &N, &M);
B = sqrt(N);
for(int i=1;i<=N;i++){
scanf("%d", &a[i]);
b[i]=a[i];
pos[i] = i/B;
}
sort(b+1,b+N+1);
int tot=unique(b+1,b+N+1)-b-1;
for(int i=1;i<=N;i++){
int tmp=lower_bound(b+1,b+tot+1,a[i])-b;
a[i]=tmp;
}
for(int i=0;i<M;i++){
scanf("%d%d", &q[i].L, &q[i].R);
q[i].id = i;
q2[i].L = q[i].L, q2[i].R = q[i].R;
}
sort(q, q+M);
for(int i=0,L=1,R=0;i<M;i++){
while(R<q[i].R) update(++R, +1);
while(R>q[i].R) update(R--, -1);
while(L<q[i].L) update(L++, -1);
while(L>q[i].L) update(--L, +1);
ANS[q[i].id]=ans;
}
for(int i = 0; i < M; ++i) {
int len = q2[i].R - q2[i].L + 1;
int oth = len - ANS[i];
printf("%d\n", max(1, ANS[i] - oth));
}
return 0;
}
E. Baby Ehab's Hyper Apartment
读了下题面就放弃了,一脸我不会做的样子,改天再补吧。