BZOJ4382: [POI2015]Podział naszyjnika

77 篇文章 0 订阅

维护最小差的时候没更新….

随便画一个环手玩,发现对于每种颜色,两个分隔点一定是在这种颜色某两个点中间,我们用每种颜色对这些点标号,每个点有k个标号,可以发现如果两个分割点标号相同,他们就能作为一组答案,hash就行了(貌似单hash会被卡

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void down(int &x,const int &y){if(x>y)x=y;}
const int maxn = 1100000;
const ll hash1 = 1e8+7;
const ll hash2 = 2333333;

int n,m,c[maxn];
int col[maxn],cn[maxn];
ll pw1[maxn],pw2[maxn];
struct node
{
    ll h1,h2;
    int i;
}a[maxn];
inline bool cmp(const node x,const node y){return x.h1==y.h1?x.h2<y.h2:x.h1<y.h1;}
inline bool cmpi(const node x,const node y){return x.i<y.i;}

int cal(const int x,const int y){return abs(n-2*(y-x));}
ll cals(const ll x){return x*(x-1)/2;}

int main()
{
    scanf("%d%d",&n,&m);
    pw1[0]=pw2[0]=n+1;
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&c[i]),cn[c[i]]++;
        pw1[i]=pw1[i-1]*pw1[0]%hash1,pw2[i]=pw2[i-1]*pw2[0]%hash2;
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=a[i-1]; a[i].i=i;
        int now=c[i];
        a[i].h1-=pw1[now]*col[now]%hash1;
        a[i].h2-=pw2[now]*col[now]%hash2;
        col[now]++; if(col[now]==cn[now]) col[now]=0;
        (a[i].h1+=pw1[now]*col[now]%hash1)%=hash1; if(a[i].h1<0) a[i].h1+=hash1;
        (a[i].h2+=pw2[now]*col[now]%hash2)%=hash2; if(a[i].h2<0) a[i].h2+=hash2;
    }sort(a+1,a+n+1,cmp);

    ll ans1=0; int ans2=n+1;
    for(int i=1;i<=n;i++)
    {
        int j; for(j=i+1;j<=n&&a[j].h1==a[i].h1&&a[j].h2==a[i].h2;j++);j--;
        if(i==j) continue;
        ans1+=cals(j-i+1);
        sort(a+i,a+j+1,cmpi);
        int head=i,las;
        for(int l=i+1;l<=j;l++)
        {
            int temp;
            las=cal(a[head].i,a[l].i);
            while(head+1<l&&(temp=cal(a[head+1].i,a[l].i))<las) head++,las=temp;
            down(ans2,las=cal(a[head].i,a[l].i));
        }
        i=j;
    }
    printf("%lld %d\n",ans1,ans2);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值