[bzoj 4382--POI2015]Podział naszyjnika

本文探讨了一道关于项链切割的复杂算法问题,通过将项链视为由多种颜色珠子组成的环形结构,研究如何切割使得每种颜色的珠子只出现在一条链中,并求解方案数量及两段链长度差的最小值。文章详细介绍了使用hash压缩和单调队列解决该问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

长度为n的一串项链,每颗珠子是k种颜色之一。 第i颗与第i-1,i+1颗珠子相邻,第n颗与第1颗也相邻。
切两刀,把项链断成两条链。要求每种颜色的珠子只能出现在其中一条链中。 求方案数量(保证至少存在一种),以及切成的两段长度之差绝对值的最小值。

首先我们可以发现如果只考虑一种颜色,那么只要相邻的两颗颜色相同的之间选两个间隔断开就可以了,这样如果有x颗颜色相同,就会有x个区间,每一个区间里的任意两个间隔都可以满足条件。
现在要使全部颜色满足条件,就只需要那两个断点都同时存在于k种颜色中每一种颜色的同一个区间。有一个很显然的性质,那就是每一个间隔都可以属于每一种颜色的一个且仅一个区间内。那每一个间隔都可以用一个k位数表示,如果两个间隔的k位数相同,那么它们就可以作为一种方案。
那么这题就算做完了,k位数可以用hash压缩,而求最小值可以用单调队列。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<queue>
#define P 1333331
using namespace std;
deque<int>q;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
struct node
{
    unsigned long long d;int p;
}wy[1000010];
bool cmp(node a,node b)
{
    if(a.d==b.d)return a.p<b.p;
    else return  a.d<b.d;
}
int id[1000010],cnt[1000010],ul[1000010];
unsigned long long bin[1000010];
int main()
{
    //freopen("4382.in","r",stdin);
    //freopen("4382.out","w",stdout);
    int n=read(),k=read();
    bin[0]=1;for(int i=1;i<k;i++)bin[i]=bin[i-1]*P;
    for(int i=1;i<=n;i++){id[i]=read(),cnt[id[i]]++;}
    unsigned long long now=0;
    for(int i=1;i<=k;i++)now+=cnt[i]*bin[i-1],ul[i]=cnt[i];
    for(int i=1;i<=n;i++)
    {
    	now-=bin[id[i]-1]*cnt[id[i]];
    	cnt[id[i]]--;if(!cnt[id[i]])cnt[id[i]]=ul[id[i]];
    	now+=bin[id[i]-1]*cnt[id[i]];
    	wy[i].d=now,wy[i].p=i;
    }sort(wy+1,wy+n+1,cmp),wy[n+1].d=0;
    int ans=n,lt=1,tot=0;long long sum=0;
    for(int i=1;i<=n+1;i++)
    {
        if(wy[i].d!=wy[i-1].d)
        {
            sum+=1LL*tot*(tot-1)/2;tot=0;
            q.clear();
            for(int j=lt;j<i;j++)
            {
                while(q.size()>1)
                {
                    if(abs(n-2*(wy[j].p-q[0]))>=abs(n-2*(wy[j].p-q[1])))q.pop_front();
                    else break;
                }
                if(q.size()>0)ans=min(ans,abs(n-2*(wy[j].p-q[0])));
                q.push_back(wy[j].p);
            }
            lt=i;
        }tot++;
    }
    printf("%lld %d\n",sum,ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值