A题
题意:问能否找出一个子序列使得其积不是完全平方数
解:只要找到一个不是完全平方的元素就可以找到这样的子序列
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); double eps = 1e-5; int t = sc.nextInt(); while(t-- != 0) { int n = sc.nextInt(); boolean f = false; while(n-- != 0) { int x = sc.nextInt(); if(Math.abs((int)Math.sqrt(x) * (int)Math.sqrt(x) - x) < eps) continue; //System.out.println("asdlfjs"); f = true; } if(f) System.out.println("YES"); else System.out.println("NO"); } } }
B题
题意:问有多少个长度为n的数组满足:
所有元素都在0~(2^k)-1,所有元素and的值为0,元素的和尽可能大
解:n个数中,每个数字二进制下都有k位,对于每一位,和尽可能大且and值为0的操作是有一个数字的这一位是0,其余都是1。答案是n^k
import java.util.Scanner; public class Main { final static long p = (long)1e9 + 7; public static long ksm(long a,long b) { long s = 1; while(b != 0) { if((b & 1) != 0) s = s * a % p; a = a * a % p; b >>= 1; } return s % p; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int t = sc.nextInt(); while(t-- != 0) { long x = sc.nextLong(); long y = sc.nextLong(); System.out.println(ksm(x,y)); } } }
C题
题意:给出n,从[1,2,3...n-1]中找出最长的子序列,使其乘机x%n = 1
解:首先,假定我们选择的序列包含数a,设其他数的乘积为x(mod n),那么就有a∗x≡1(modn),也就是x是a的逆元。那么a就要满足gcd(a,n)=1,否则a没有逆元,不可能入选
把所有与n互质的数在模n意义下乘起来,可以得到一个小于n的数ans,ans一定与n互质。那么ans一定在前面出现过,如果ans≠1,那么在答案序列中把那个数删掉就行了。
假设出了ans以外,其他数的乘积为y,那么就有ans ∗ y mod n = ans,得到y mod n=1。
import java.util.Scanner; public class Main { public static int gcd(int a,int b) { if(b == 0) return a; return gcd(b,a % b); } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int t = 1; int[] a = new int[100010]; int n = sc.nextInt(); long ans = 1; for(int i = 1;i <= n; ++i) if(gcd(i,n) == 1) { a[t++] = i; ans = ans * i % n; } if(ans != 1) { boolean f = false; System.out.println(t - 2); for(int i = 1;i < t; ++i) { if(a[i] == ans) continue; if(f == true) System.out.print(" "); f = true; System.out.print(a[i]); } System.out.println(); } else { System.out.println(t - 1); for(int i = 1;i < t; ++i) System.out.print(a[i] + ((i == t - 1)?"\n":" ")); } } }
D题
题意:长度为n的数组和q个询问,每次询问区间[l,r]里的数最少要分成几组才能使每组内出现次数最多的都不超过(n/2向上取整)
解:
可以用vector记录每个数出现的位置,然后二分找出在某个区间内出现的次数。
怎样知道在这个区间内哪个数的出现次数最多呢?
线段树维护
假设区间有x个数,出现次数最多的数字出现了f次
f <= (x/2向上取整) 1
f > (x/2向上取整) 剩下的x-f最多可以与x-f+1个数合成一组,剩下的每个数都自己一组,那么就是f-(x-f+1) + 1 = 2*f-x
#include<bits/stdc++.h> using namespace std; const int maxn = 3e5 + 10; #define lt(x) x << 1 #define rt(x) x << 1 | 1 int n,q,s,e; vector<int>v[maxn]; int a[maxn],t[maxn * 4]; int getnum(int l,int r,int x) { return upper_bound(v[x].begin(),v[x].end(),r) - lower_bound(v[x].begin(),v[x].end(),l); } int mymax(int x,int y,int l,int r) { int t1 = getnum(l,r,x),t2 = getnum(l,r,y); if(t1 > t2) return x; return y; } void build(int x,int l,int r) { if(l == r) { t[x] = a[l]; return; } int mid = (l + r) >> 1; build(lt(x),l,mid); build(rt(x),mid + 1,r); t[x] = mymax(t[lt(x)],t[rt(x)],l,r); } int query(int x,int l,int r) { if(s <= l && r <= e) return getnum(s,e,t[x]); int mid = (l + r) >> 1; int t1 = 0,t2 = 0; if(s <= mid) t1 = query(lt(x),l,mid); if(e > mid) t2 = query(rt(x),mid + 1,r); // cout<<t1<<' '<<t2<<endl; return max(t1,t2); } int main() { std::ios::sync_with_stdio(false); cin>>n>>q; for(int i = 1;i <= n; ++i) { cin>>a[i]; v[a[i]].push_back(i); } build(1,1,n); while(q--) { cin>>s>>e; cout<<max(1,2 * query(1,1,n) - (e - s + 1))<<'\n'; } return 0; }