1. 程式人生 > >Codeforces 980D Perfect Groups

Codeforces 980D Perfect Groups

多少 情況 space 素數篩選 質因數 我們 max clas memset

題目大意

設有一個數組A和對A的一個操作f。那麽f(A)返回一個最小的k,使得A能分成k組(每組元素不一定連續,但每個元素一定屬於某一組),且每組裏的任意兩個元素的乘積是完全平方數。
現有一個長度為n的數組A,問有多少中情況,從A中取出一段連續的子序列B,使f(B)=k(k = 1...n)

解題思路

首先考慮操作f中的某一組中的某兩個數a和b。因為\[ab = k^2(k \in Z)\]所以我們有\[a = \alpha^2t_1\]\[b=\beta^2t_2\](α、β為整數且t1、t2不含平方因子)
由質因數分解易推出t1 = t2
所以那一組中的所有數去掉平方因子後都相等
又因為分的組數要最小,所以任意兩個數若去掉平方因子後相等,那麽他們一定在同一組中。

好了,在回頭看如何實現題目要求。
首先第一步一定是去掉每個數的平方因子,記在a數組。之後,我們有一個巧妙的預處理。我們用f[i]保存在i前面第一個a[i] = a[f[i]]的位置。那麽在一個區間中若a[i]不為0且f[i]小於這個區間的左端點,就一定不能與它之前的數並為一組,否則一定可以。然後看如何求答案。若我們對於每個k,都枚舉區間左端點,再進行推進,那麽效率就有些低了,有可能會TLE。所以我們換個角度思考。如果枚舉左端點,然後一邊按順序掃左端點後面的點,一邊記錄到最後答案裏,那麽時間復雜度就只有O(n^2)。問題解決。

程序

#include<bits/stdc++.h>
using namespace std;
map<int, int> ct;//map能更快地完成初始化,也可以用O(n^2)的時間暴力初始化
int n, a[10010], f[10010], ans[10010], cnt;//a、f的意義如上面所說,ans保存最終答案
int l = 0, b[10010], p[10010];//p保存素數,b為臨時數組
int main(){
    memset(b, 0, sizeof(b));
    memset(ans, 0, sizeof(ans));
    memset(p, 0, sizeof(p));
    memset(f, 0, sizeof(f));
    memset(a, 0, sizeof(a));
    n = 0;
    cnt = 0;
    l = 0;
    for(int i = 2; i < 10010; i++){
        if(b[i] == 0){
            l++; p[l] = i;
        } else continue;
        for(int j = i + i; j < 10010; j += i)
            if(b[j] == 0) b[j] = 1;
    }//以上完成素數篩選
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++)
        for(int j = 1; (j <= l) && ((p[j] * p[j]) <= abs(a[i])); j++){//小心有負數
            while(a[i] != 0 && a[i] % (p[j] * p[j]) == 0) a[i] /= (p[j] * p[j]);
            if(a[i] == 0 || a[i] == 1 || a[i] == -1) break;//已經結束,不如退出
        }
        //以上完成去除所有數的平方因子
    for(int i = 1; i <= n; i++){
        f[i] = ct[a[i]];
        ct[a[i]] = i;
    }//完成初始化
    for(int i = 1; i <= n; i++){
        cnt = 0;
        for(int j = i; j <= n; j++){
            if(a[j] && f[j] < i) cnt++; //註意0的情況
            ans[max(1, cnt)]++;
        }
    }//統計答案
    for(int i = 1; i <= n; i++) 
        if(i == 1) printf("%d", ans[i]); else printf(" %d", ans[i]);
    printf("\n");
    return 0;
}

Codeforces 980D Perfect Groups