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

#172. 【WC2016】论战捆竹竿

这是一个美好的下午,小 W 和小 C 在竹林里切磋捆竹竿的技艺。

竹林里有无数根完全一样的短竹子,每一根竹子由 nn 节组成。

这些竹子比较特别,每一节都被染上了颜色。可能的颜色一共 2626 种,分别用小写英文字母 a 到 z 表示。也就是说,如果把竹子的底端到顶端的颜色按顺序写出来可以排成一个由小写英文字母组成的字符串。

小 W 和小 C 都是捆竹竿的高手,他们知道怎样才能把零散的短竹子捆成一整根长竹竿。初始时你拿着一根短竹子作为当前的竹竿。每次你可以选择一根短竹子,短竹子底端若干节(可以是 00 节)与竹竿的最上面若干节对应地一节一节捆起来,而短竹子前面剩下的节伸出去,这样就得到了一根更长的竹竿。注意,竹子的底端是靠近根部的那一端,不可以颠倒。

小 W 对竹竿的审美要求很高,他捆竹竿时有一个癖好:如果两根竹子的某两节被捆在了一起,那么它们的颜色必须相同。

我们假设一根短竹子从底端到顶端每节的颜色为 aba。

那么两根竹子可以首尾捆在一起,可以得到一根颜色为 abaaba 的竹竿;也可以将第一根顶端的一节 a 与第二根底端的一节 a 捆在一起,得到一根颜色为 ababa 的竹竿;还可以直接将每一节都对应起来,捆成一根颜色为 aba 的竹竿。

假设我们在颜色为 ababa 的竹竿顶端再捆一根竹子,则可以捆成 ababaaba,abababa 和 ababa 三种不同的情况。

但是小 C 在这个问题上有不同的看法,他认为小 W 捆不出很多种长度不同的竹竿。小 W 非常不服,于是他找到了你——现在请你求出在竹竿长度不超过 ww 的情况下,小 W 可以捆出多少种长度不同的竹竿。其中,竹竿的长度指从底端到顶端的竹子的节的个数。

注意:如果 w<nw<n,则没有合法的长度,此时答案为 00。

输入格式

第一行包含 11 个正整数 TT,为数据组数。

每组数据的第一行包含 22 个正整数 nn 和 ww,表示短竹子的长度和竹竿的长度上限。

每组数据的第二行包含一个长度为 nn 的字符串,该字符串仅由小写英文字母构成,表示短竹子从底端到顶端每节的颜色。

输出格式

输出共 TT 行,每行包含一个整数表示捆成竹竿的不同长度种数。

样例一

input

1
4 11
bbab

output

5

explanation

可以捆成长度不超过 1111 的竹竿有 66 种不同的情况:

  1. bbab
  2. bbabbab
  3. bbabbbab
  4. bbabbabbab
  5. bbabbabbbab
  6. bbabbbabbab

后两种竹竿长度相同,因此不同长度的竹竿共有 55 种。长度分别为:4,7,8,10,114,7,8,10,11。

样例二

input

2
44 1000
baaaaaabaabbaaabbbbabbbaaabbbababaaabaaabaaa
41 1000
abaabbabaaabaabbbbbbbbbbbababbbbaaabaabbb

output

195
24

样例三

见样例数据下载。

限制与约定

对于所有的测试数据,保证所有的字符串均由小写字母构成,保证 T=5T=5。

各测试点满足以下约定:

测试点编号 nn ww 约束
1 ≤10≤10 ≤10≤10 ss 仅包含字母 a 和 b
2 ≤20≤20 ≤20≤20
3 ≤100≤100 ≤1018≤1018
4
5 ≤103≤103
6
7 ≤5×104≤5×104 ≤105≤105
8
9
10
11 ≤7×104≤7×104 ≤1018≤1018
12
13 ≤105≤105
14
15
16
17 ≤5×105≤5×105
18
19
20

时间限制:1s1s

空间限制:256MB

/*
    可能是因为string导致7~10爆了空间
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500010
using namespace std;
int n,w,nxt[maxn],ans;
string s;
bool vis[maxn];
void getnxt(){
    int i=0,j=-1;
    nxt[0]=-1;
    while(i!=n){
        if(s[i]==s[j]||j==-1)nxt[++i]=++j;
        else j=nxt[j];
    }
}
void dfs(string c,int len){
    if(!vis[len]&&len<=w)vis[len]=1,ans++;
    else return;
    int j=nxt[n];
    while(j){
        dfs(c+s.substr(j,n-j),len+(n-j));
        j=nxt[j];
    }
    dfs(c+s,len+n);
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        memset(vis,0,sizeof(vis));ans=0;
        scanf("%d%d",&n,&w);cin>>s;
        getnxt();
        dfs(s,n);
        printf("%d\n",ans);
    }
}

10分 kmp暴力

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
inline int read()
{
    int t=1,sum=0;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)
    {
        if(ch==‘-‘) t=-1;
        ch=getchar();
    }
    while(ch>=‘0‘&&ch<=‘9‘)
        sum=sum*10+ch-‘0‘,ch=getchar();
    return t*sum;
}
const int Max=1e6+10;
const LL inf=1e18;
int nxt[Max],q[Max],Q[Max],len[Max],n,nm;
LL f[Max],w[Max],W; char s[Max];
void get_nxt()
{
    int i,j;
    for(nxt[1]=j=0,i=2;i<=n;i++)
    {
        while(j&&s[j+1]!=s[i]) j=nxt[j];
        nxt[i]=s[j+1]==s[i]?++j:j;
    }
}
int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
void change(int x)
{
    int d=gcd(x,nm),i,j,tot; static LL pre[Max];
    for(i=0;i<nm;i++) pre[i]=f[i];
    for(i=0;i<x;i++) f[i]=inf;
    for(i=0;i<nm;i++)
        if(pre[i]!=inf)
            j=pre[i]%x,f[j]=min(f[j],pre[i]);
    for(i=0;i<d;i++)
    {
        tot=0; j=i;
        while(true)
        {
            q[++tot]=j;
            j=(j+nm)%x;
            if(j==i) break;
        }
        for(j=1;j<=tot;j++) q[tot+j]=q[j];
        tot<<=1;
        for(j=2;j<=tot;j++) f[q[j]]=min(f[q[j]],f[q[j-1]]+nm);
    }
    nm=x;
}
void update(int a0,int d,int num)
{
    int D=gcd(d,a0),i,j,k,tot,N,l,r;
    static LL w[Max],mi; change(a0);
    for(i=0;i<D;i++)
    {
        tot=0; j=i;
        while(true)
        {
            q[++tot]=j;
            j=(j+d)%a0;
            if(j==i) break;
        }
        for(mi=inf,j=1;j<=tot;j++) if(f[q[j]]<mi) mi=f[q[j]],k=j;
        if(mi==inf) continue;
        else N=0;
        for(j=k;j<=tot;j++) Q[++N]=q[j];
        for(j=1;j<k;j++) Q[++N]=q[j];
        q[1]=l=r=1; w[1]=f[Q[1]]-d;
        for(j=2;j<=N;j++)
        {
            if(q[l]+num<=j) l++;
            if(l<=r) f[Q[j]]=min(f[Q[j]],w[l]+1LL*j*d+a0);
            while(l<=r&&w[r]>=f[Q[j]]-1LL*j*d) r--;
            q[++r]=j; w[r]=f[Q[j]]-1LL*j*d;
        }
    }
}
int main()
{
    int T=read(),i,j,k; LL ans;
    while(T--)
    {
        n=read(); scanf("%lld",&W); W-=n;
        scanf("%s",s+1); get_nxt();
        if(W<0)
        {
            puts("0");
            continue;
        }
        for(j=0,i=nxt[n];i;i=nxt[i]) len[++j]=n-i;
        for(i=1;i<=(j>>1);i++) swap(len[i],len[j-i+1]);
        for(f[0]=0,i=1;i<n;i++) f[i]=inf;
        nm=n;
        for(i=1;i<=j;i=k)
        {
            if(i==j)
            {
                update(len[i],1,1);
                break;
            }
            for(k=i+1;k<=j&&len[i]-len[i+1]==len[k-1]-len[k];k++);
            update(len[k-1],len[i]-len[i+1],k-i);
        }
        for(ans=i=0;i<nm;i++)
            if(f[i]!=inf&&f[i]<=W)
                ans+=(W-f[i])/nm+1;
        printf("%lld\n",ans);
    }
    return 0;
}

100分 完全不明白

时间: 2024-08-28 14:08:29

uoj #172. 【WC2016】论战捆竹竿的相关文章

luogu P4156 [WC2016]论战捆竹竿

传送门 官方题解(证明都在这) 神仙题鸭qwq 转化模型,发现这题本质就是一个集合,每次可以加上集合里的数,问可以拼出多少不同的数 首先暴力需要膜意义下的最短路,例题戳这 然后这个暴力可以优化成N^2的.具体操作是枚举每个数,然后从某个点只用这个数往后跳,这样在膜m意义下可以形成\(gcd(a,m)\)个环.每个环找到dis最小的点,从这个点开始依次遍历整个环,更新后一个位置 有个结论是集合中的数可以分成\(logn\)个等差数列,所以可以每个等差数列贡献答案 然后对于每个等差数列,先把膜m意义

「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

【QQ技术】群文件报毒怎样下载?~ 变相绕过QQ复杂检验过程

刚才又人问我,要是群文件被鉴定为病毒那怎么下载? 我简单说一下吧: 其实qq客户端过滤比较严的,而web段却还是老一套,很多人说出现这个情况其实是腾讯已经把他库里面的文件删了,其实不然 如果源删了,那么web段也就不可能下载到了,咱们从这方面绕过它的验证(QQ群空间 http://qun.qzone.qq.com/group) 看图: 收工 扩展: 上传绕过:其实上传绕过太简单了,比如设置一个压缩包密码,比如压缩包里面套压缩包等等的,都是可以绕过的 赠送: 外网突破-打开就能用 http://p

UOJ 171 【WC2016】挑战NPC

一开始还真没想到是一般图匹配这种模型(毕竟才会的带花树) 把每一个盒子拆成3个,每一个可以放置进它的小球分别向这三个点连边,然后这三个点在连成一个三元环,最终答案就是小球数目-匹配数. 由于是一般图,所以套一个带花树就可以了. NOTICE:寻找增广路时,应该从球先找起,这样子才保证了每个球有地方放置. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<vector&g

uoj#87. mx的仙人掌

1 //Achen 2 #include<bits/stdc++.h> 3 #define For(i,a,b) for(int i=(a);i<=(b);i++) 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--) 5 #define Formylove return 0 6 const int N=2e6+7,mod=571111; 7 typedef long long LL; 8 typedef double db; 9 using

【UOJ】【UR #2】猪猪侠再战括号序列(splay/贪心)

http://uoj.ac/problem/31 纪念伟大的没有调出来的splay... 竟然那个find那里写错了!!!!!!!!!!!!! 以后要记住:一定要好好想过! (正解的话我就不写了,太简单了.. #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> #

在开发板Linux上挂载"驱动"挂载不成功,出现提示server 172.27.52.100 not responding, still trying

1.在开发板具体操作步骤如下: 1.1 :设置IP ifconfig eth0 172.27.52.200 1.2 :ping通 虚拟机Linux 主机Linux ping XXX.XXX.XXX.XXX 1.3.挂接 mount -t nfs -o nolock  XXX.XXX.XXX.XXX:/work/nfs_root/first_fs  /mnt // 例如:mount -t nfs -o nolock  172.27.52.100:/work/nfs_root/first_fs  /

[UOJ#220][BZOJ4651][Noi2016]网格

试题描述 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐,其余的格子中,每个格子有一只跳蚤. 我们称占据的格子有公共边的两只跳蚤是相邻的. 我们称两只跳蚤是连通的,当且仅当这两只跳蚤相邻,或存在另一只跳蚤与这两只跳蚤都连通. 现在,蛐蛐国王希望,将某些(0 个,1 个或多个)跳蚤替换成蛐蛐,使得在此之后存在至少两只跳蚤不连通. 例如:我们用图表示一只跳蚤,用图表示一只蛐蛐,那么图 1 描述了一个 n=4,m

UOJ 52 元旦激光炮

http://uoj.ac/problem/52 题意:每次可以得到3个序列中 思路:每次分别取出三个序列的K/3长度的位置,取最小的那个,然后每次减掉它,总复杂度是Nlog3N #include "kth.h" #include<algorithm> int C[2000005]; int query_kth(int n_a, int n_b, int n_c, int k) { int Len=k; int la=0,lb=0,lc=0; int ans=0; whil