BZOJ4382[POI2015] Podział naszyjnika

BZOJ4382[POI2015] Podział naszyjnika

Description

长度为n的一串项链,每颗珠子是k种颜色之一。 第i颗与第i-1,i+1颗珠子相邻,第n颗与第1颗也相邻。

切两刀,把项链断成两条链。要求每种颜色的珠子只能出现在其中一条链中。

求方案数量(保证至少存在一种),以及切成的两段长度之差绝对值的最小值。

Input

第一行n,k(2<=k<=n<=1000000)。颜色从1到k标号。

接下来n个数,按顺序表示每颗珠子的颜色。(保证k种颜色各出现至少一次)。

Output

一行两个整数:方案数量,和长度差的最小值

Sample Input

9 5

2 5 3 2 2 4 1 1 3

Sample Output

4 3

HINT

四种方法中较短的一条分别是(5),(4),(1,1),(4,1,1)。相差最小值6-3=3。

Solution:

考虑怎么样的情况下可以分割。我们按照前缀给环上可以分割的点进行编号:

1

可以发现如果前缀相同,此两点之间就可以断开,当然,因为是环形,最后一个该颜色点之后该颜色的点前缀就应编号为0。于是对于每个点都对应一个k 元组(sum1,sum2,sum3,sum4,sum5...sumk) ,当且仅当两个k 元组相同时,这两点可以连边。于是考虑哈希,这道题就解的差不多了,注意一下Hash值的累加用O(1)转移,至于最小值的话,根据单调性随便弄一下就好了。

这题还卡哈希,用了双哈希才过。

#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define B1 200019
#define B2 200011
#define M 1000005
#define P1 1000000007
#define P2 1000000009
#define ll long long
using namespace std;
struct Node{
    int id,res1,res2;
    bool operator <(const Node &a)const{
        if(res1!=a.res1)return res1<a.res1;
        else if(res2!=a.res2)return res2<a.res2;
        else return id<a.id;
    }
}Q[M];
int A[M],Base1[M],Base2[M],ed[M],cnt[M];
void Rd(int &res){
    char c;res=0;
    while(c=getchar(),!isdigit(c));
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),isdigit(c));
}
int main(){
    int n,k;
    Rd(n);Rd(k);
    for(int i=1;i<=n;i++)Rd(A[i]);
    Base1[0]=Base2[0]=1;
    for(int i=1;i<=k;i++){
        Base1[i]=(1LL*Base1[i-1]*B1)%P1;
        Base2[i]=(1LL*Base2[i-1]*B2)%P2;
    }
    memset(ed,-1,sizeof(ed));
    for(int i=n;i>=1;i--){
        if(~ed[A[i]])continue;
        ed[A[i]]=i;
    }
    int res1=0,res2=0;
    for(int i=1;i<=n;i++){
        cnt[A[i]]++;
        res1=(res1+Base1[A[i]])%P1;
        res2=(res2+Base2[A[i]])%P2;
        if(ed[A[i]]==i)res1=((res1-1LL*Base1[A[i]]*cnt[A[i]])%P1+P1)%P1;
        if(ed[A[i]]==i)res2=((res2-1LL*Base2[A[i]]*cnt[A[i]])%P2+P2)%P2;
        Q[i].id=i;Q[i].res1=res1;Q[i].res2=res2;
    }
    sort(Q+1,Q+n+1);
    int Half=(n+1)/2,ans=n;
    ll cnt=0;
    for(int i=1;i<=n;){
        int nxt=i;
        while(nxt<=n&&Q[nxt].res1==Q[i].res1&&Q[nxt].res2==Q[i].res2)nxt++;
        cnt+=1LL*(nxt-i)*(nxt-i-1)/2;
        for(int L=i,R=i;R<nxt;R++){
            while(L<R&&Q[R].id-Q[L].id>=Half)L++;
            int rs1=abs(n-2*(Q[R].id-Q[L].id));
            if(L>i){
                int rs2=abs(n-2*(Q[R].id-Q[L-1].id));
                if(rs2<rs1)rs1=rs2;
            }
            if(rs1<ans)ans=rs1;
        }
        i=nxt;
    }
    cout<<cnt<<' '<<ans<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章