luogu P4156 [WC2016]论战捆竹竿

传送门

官方题解(证明都在这)

神仙题鸭qwq

转化模型,发现这题本质就是一个集合,每次可以加上集合里的数,问可以拼出多少不同的数

首先暴力需要膜意义下的最短路,例题戳这

然后这个暴力可以优化成N^2的.具体操作是枚举每个数,然后从某个点只用这个数往后跳,这样在膜m意义下可以形成\(gcd(a,m)\)个环.每个环找到dis最小的点,从这个点开始依次遍历整个环,更新后一个位置

有个结论是集合中的数可以分成\(logn\)个等差数列,所以可以每个等差数列贡献答案

然后对于每个等差数列,先把膜m意义转成膜\(a_1\)的最短路.具体操作是对用已知dis值更新部分答案,然后从最小dis处一直往后跳m更新其他的点.然后对于每个位置可以从前面的一段区间转移过来(代价为距离*公差),可以单调队列优化

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define il inline
#define re register

using namespace std;
const int N=5e5+10;
il LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int m,a[N];
char cc[N];
LL n,w,di[N],dd[N];
uLL hl[N],hr[N],bb[N],bs=377;
int ff[N];
il int findf(int x){return ff[x]==x?x:ff[x]=findf(ff[x]);}
il int gcd(int a,int b){return b?gcd(b,a%b):a;}
LL q[N][2],hd,tl;
il void cg(LL *di,int i,int k,int md,LL cw,LL ww,int mm)
{
    hd=1,q[tl=1][0]=di[i]-ww,q[tl][1]=1;
    LL jj=2;
    for(int j=(i+k)%md;j^i;j=(j+k)%md,++jj)
    {
        while(hd<=tl&&jj-q[hd][1]+1>mm) ++hd;
        di[j]=di[j]<q[hd][0]+1ll*jj*ww+cw?di[j]:q[hd][0]+1ll*jj*ww+cw;
        while(hd<=tl&&q[tl][0]>=di[j]-1ll*jj*ww) --tl;
        q[++tl][0]=di[j]-1ll*jj*ww,q[tl][1]=jj;
    }
}

int main()
{
    bb[0]=1;
    for(int i=1;i<=N-10;++i) bb[i]=bb[i-1]*bs;
    int T=rd();
    memset(dd,100,sizeof(dd));
    while(T--)
    {
        n=rd(),w=rd()-n;
        scanf("%s",cc+1);
        for(int i=1;i<=n;++i) hl[i]=hl[i-1]*bs+cc[i];
        hr[n+1]=0;
        for(int i=n;i;--i) hr[i]=bb[n-i]*cc[i]+hr[i+1];
        m=0;
        for(int i=1;i<n;++i)
            if(hl[n-i]==hr[i+1]) a[++m]=i;
        for(int i=1;i<=m+2;++i) ff[i]=i;
        ff[0]=1;
        memset(di,0x3f3f3f,sizeof(di));
        di[0]=0;
        LL md=n;
        for(int cn=0;cn<m;)
        {
            int ii=findf(0),k=1,dt=0;
            ++cn,ff[ii]=ii+1;
            for(int j=findf(ii);j<=m;j=findf(j))
            {
                if(!dt) dt=a[j]-a[ii],++cn,++k,ff[j]=j+1;
                else if(a[j]==a[ii]+k*dt) ++cn,++k,ff[j]=j+1;
                else break;
            }
            LL x=a[ii];
            for(int i=0;i<md;++i) dd[di[i]%x]=min(dd[di[i]%x],di[i]),di[i]=di[n+1];
            int sht=gcd(md,x);
            for(int i=0;i<sht;++i)
            {
                int jj=i;
                for(int j=(jj+md)%x;j^i;j=(j+md)%x)
                  if(dd[j]<dd[jj]) jj=j;
                for(int j=jj,k=(jj+md)%x;k^jj;j=(j+md)%x,k=(k+md)%x)
                    dd[k]=min(dd[k],dd[j]+md);
            }
            md=x;
            for(int i=0;i<md;++i) di[i]=dd[i],dd[i]=dd[n+1];
            sht=gcd(md,dt);
            for(int i=0;i<sht;++i)
            {
                int jj=i;
                for(int j=(jj+dt)%md;j^i;j=(j+dt)%md)
                    if(di[j]<di[jj]) jj=j;
                cg(di,jj,dt,md,md,dt,k);
            }
        }
        LL ans=0;
        for(int i=0;i<md;++i) ans+=max(1ll*0,(w-di[i]+md)/md);
        printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/smyjr/p/10264389.html

时间: 2024-08-30 12:11:16

luogu P4156 [WC2016]论战捆竹竿的相关文章

uoj #172. 【WC2016】论战捆竹竿

#172. [WC2016]论战捆竹竿 这是一个美好的下午,小 W 和小 C 在竹林里切磋捆竹竿的技艺. 竹林里有无数根完全一样的短竹子,每一根竹子由 nn 节组成. 这些竹子比较特别,每一节都被染上了颜色.可能的颜色一共 2626 种,分别用小写英文字母 a 到 z 表示.也就是说,如果把竹子的底端到顶端的颜色按顺序写出来可以排成一个由小写英文字母组成的字符串. 小 W 和小 C 都是捆竹竿的高手,他们知道怎样才能把零散的短竹子捆成一整根长竹竿.初始时你拿着一根短竹子作为当前的竹竿.每次你可以

「WC2016」论战捆竹竿

「WC2016」论战捆竹竿 前置知识 参考资料:<论战捆竹竿解题报告-王鉴浩>,<字符串算法选讲-金策>. Border&Period 若前缀 \(pre(s,x)?\) 与后缀 \(suf(s,n-x-1)?\) 相等,则 \(pre(s, x)?\) 是 \(s?\) 的一个 \(\text{Border}?\). \(x?\) 是 \(s?\) 的一个周期 (\(\text{Preiod}?\)) 满足 \(s[i]=s[i+x],\forall{1\leq i\le

luogu P2863 [USACO06JAN]牛的舞会The Cow Prom

https://www.luogu.org/problem/show?pid=2863#sub 题目描述 The N (2 <= N <= 10,000) cows are so excited: it's prom night! They are dressed in their finest gowns, complete with corsages and new shoes. They know that tonight they will each try to perform th

luogu P2639 [USACO09OCT]Bessie的体重问题Bessie&#39;s We…

题目描述 Bessie像她的诸多姊妹一样,因为从Farmer John的草地吃了太多美味的草而长出了太多的赘肉.所以FJ将她置于一个及其严格的节食计划之中.她每天不能吃多过H (5 <= H <= 45,000)公斤的干草. Bessie只能吃一整捆干草:当她开始吃一捆干草的之后就再也停不下来了.她有一个完整的N (1 <= N <= 500)捆可以给她当作晚餐的干草的清单.她自然想要尽量吃到更多的干草.很自然地,每捆干草只能被吃一次(即使在列表中相同的重量可能出现2次,但是这表示

luogu P3799 妖梦拼木棒

二次联通门 : luogu P3799 妖梦拼木棒 /* luogu P3799 妖梦拼木棒 用一个桶存下所有的木棒 美剧两根短的木棒长度 后随便乘一乘就 好了.. */ #include <algorithm> #include <cstdio> #define Mod 1000000007 #define Max 5000 void read (int &now) { now = 0; register char word = getchar (); while (wo

[luogu P1967][NOIp2013]P1967 货车运输

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z

luogu 3126 回文的路径

https://www.luogu.org/problem/show?pid=3126 考虑dp,从两头走到中间. f[i][j][k][l]表示从左上角走到(i,j),从右下角走到(k,l),路径长度相等,所经过路径相同的方案数. 方程不再赘述. 考虑步数要相同,所以只要枚举步数和行就好. f[i][j][k]表示第一个点在第j行,第2个点在第k行,走i步的方案数. 所以得出方程f[i][j][k]=(f[i-1][j-1][k]+f[i-1][j][k+1]+f[i-1][j-1][k+1]

luogu P2018 消息传递

二次联通门 : luogu P2018 消息传递 /* luogu P2018 消息传递 树形dp 原来用优先队列做了一下, T了俩点 MMP 去看正解.. 复杂度一样好不好.. 每次到达一个点,记录其子树中所有的dp值 优先向大的一边转移 */ #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define INF 1e8 const int BUF

luogu P1966 火柴排队

二次联通门 : luogu P1966 火柴排队 /* luogu P1966 火柴排队 神TM逆序对... noip怎么这么坑啊.. 暴力都没得打 此题模拟考试时爆了0 做法 将A数组排序,由于B数组与A数组是一一对应的 那么B数组的位置也会发生相应的变化 此时B数组逆序数对数即为答案 */ #include <cstdio> #include <iostream> #include <algorithm> const int BUF = 123123123; cha