Codeforces 980D

http://codeforces.com/contest/980/problem/D

大致题意,设F(S)表示在集合S中把元素划分成若干组,使得每组内元素两两相乘的结果的都是完全平方数的最小划分组数

对于给定的序列A,对于每一个k = 1..n,分别求出在A的所有子串[l, r]中有多少满足F(A[l..r]) = k

n <= 5000, abs(A[i]) <= 10^9

先考虑如何求出F(S),对于一个满足要求的组,可以发现对组内元素分解质因数后,每一个质数出现次数的奇偶性相同;

证明 : 如果两数相乘是平方数,设这个数为p1^k1*p2^k2*..*pn*kn,必然有所有k % 2 = 0,当且仅当只有奇偶性相同的两个数相加才能变成偶数,所以每一个分解质因数后每一个质数出现次数的奇偶性相同

通过这个性质,就可以推导出本题的很多做法:

做法1:考虑出现次数为偶数的质因子本身就可以构成平方数,所以导致分组不合法的只可能是出现次数为奇数的质因子,考虑对于序列中所有数分解质因数,把所有出现次数为奇数的质因子放进一个vector中

对于每一个数产生的vector,用map来给他们染色,相同的vector对应的数染成一个颜色,如果说一些数对应的颜色相同,那么说明所有数的奇数质因子在两两相乘的时候都可以消去,即可以把这些数分成一组,那么问题就转变为求区间内不同的颜色数,直接扫一遍即可

做法2:还是考虑偶数因子是没有用的,实际上只需要关心每一个质因子的出现次数在模2意义下的奇偶性,所以大可以把所有数除去他们的平方因子,离散后做一遍区间数颜色即可

做法3:由做法1可以推导出,对于合法的组内的元素,他们出现次数为奇数的质因子都是相同的,那么如果说A * B为完全平方数, B * C 为完全平方数,说明A,B的奇数质因子集合相同,BC的奇数质因子集合相同,所以AC的奇数质因子集合也相同

。所以当满足AB是完全平方数,BC是完全平方数时,AC也是完全平方数,这个东西具有传递性,考虑对于每一个数A[i],只需要找到其前面最后一个能和他合并的数pre[i],对于每一个区间,答案是满足pre[i] < 左端点的i的数量

对于上述所有做法,都要记得特判0!!!

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define N (100005)
#define int ll
struct Point{ int x, id; } e[N];
int a[N], buf[N], Ans[N], n, col, f0;
inline bool cmp(Point A, Point B){ return A.x < B.x; }
inline int change(int x){
    int res = x;
    for(int i = 2; i * i <= abs(x); i++)
        while(res % (i * i) == 0) res /= i * i;
    return res;
}
main(){
    read(n);
    for(int i = 1; i <= n; i++) read(a[i]), a[i] = change(a[i]);
    for(int i = 1; i <= n; i++) e[i].x = a[i], e[i].id = i;
    sort(e + 1, e + n + 1, cmp);
    a[e[1].id] = ++col; if(e[1].x == 0) f0 = 1;
    for(int i = 2; i <= n; a[e[i++].id] = col){
        if(e[i].x > e[i-1].x) col++;
        if(e[i].x == 0) f0 = col;    
    }
    for(int i = 1; i <= n; i++){
        int res = 0;
        for(int j = i; j <= n; j++){
            if(!buf[a[j]] && a[j] != f0) res++;
            buf[a[j]]++;
            if(!res) Ans[1]++; else Ans[res]++;
        }
        for(int j = i; j <= n; j++) buf[a[j]]--;
    }
    for(int i = 1; i <= n; i++) cout << Ans[i] << " ";
    return 0;
}
萌萌哒的代码>.<

猜你喜欢

转载自www.cnblogs.com/mangoyang/p/9277575.html