首页 > 其他分享 >P4156 [WC2016]论战捆竹竿 题解

P4156 [WC2016]论战捆竹竿 题解

时间:2023-03-30 13:44:06浏览次数:54  
标签:题解 ll WC2016 P4156 字符串 include Border 短路 dis

题目链接

题意描述

给定一个字符串 \(S\),你初始拥有一个空串 \(T\),每次可以选择这个字符串的一个 Border,去掉它后接在 \(T\) 的后面,操作后 \(S\) 不变,给出一个上限 \(w\),求出在 \([1,w]\) 中有多少长度可以被拼出。

题目分析

首先可以看出,抛开字符串,把每个可以拼上去的长度筛选出来,问题就转化为有 \(k\) 个不同的数,通过加法操作可以表示出范围内的多少个数,这显然是一个同余最短路的问题。

模仿通常同余最短路的操作,从每个点延伸 \(k\) 条边后跑一边最短路即可,但是这样只能得到 30 分,原因在于边数过大。

我们考虑优化,既然边这么多,能不能去掉一些没有用的部分,反过来考虑字符串 Border 的一些性质,注意到周期好像是个很有用的部分,能把冗余部分压缩,我们试图从这方面考虑。

对于周期和 Border,我们有一个很显然的性质:每个长度大于 \(\frac{|s|}{2}\) 的 Border 剩下那部分的长度是原字符串的一个周期,而任意一个周期与一个最短周期,记为 \(p\),\(q\)(\(p>q\)),\(\gcd(p,q)\) 也是原字符串的一个周期。

因为 \(p\) 和 \(q\) 的最大公因数也是一个周期,而 \(\gcd(p,q)\leq\min(p,q)\),从上述定义可以看出 \(q=\gcd(p,q)\),因此 \(q|p\),当 \(p\) 所对应的 Border 取最小的时,即 \(p\) 最大,我们就可以发现 \(q\) 复制后只要不超过 \(p\),一定对应一个 Border,而这些 Border 刚好组成一个等差数列

因为所有的周期都能由最小周期组成(这点可以假设不成立后反证匹配得到,这里不再赘述),所以所有大于 \(\frac{|s|}{2}\) 的 Border 都构成一个等差数列,而把小于的最大的 Border 提出来变成一个新字符串继续递归,可以得到最多不超过 \(\log(n)\) 条等差数列。

这个性质很有用,我们可以把每条数列单独拉出来跑同余最短路,优化边数即可达到目的。

具体实现

把最小的当成模数,每个点就只有一条出边,整个图总共会形成 \(\gcd(a,d)\)个简单环(\(a\) 为 最小值,\(d\),为公差)。

接着把环分离,从最小值开始转圈跑最短路,最后跑下一条等差数列的时候改变一下模数即可(如果从小到大跑只用把每个最小值所在位置,改成模新模数的位置,并查看前面会不会对后面增加的同余类产生影响即可)。

一共有 \(\log(n)\) 条数列,每条数列跑最短路复杂度为 \(O(n)\),整体时间为 $ O(n \log n)$。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring>
#include<queue>
#define N 500005
using namespace std;
typedef unsigned long long ll;
ll cnt,bor[N<<3],nex[N<<3];
ll res[N<<3];
ll dis[N<<3],lim;
struct Node{
	ll num,val;
};
ll top,Q[N<<3],q2[N<<3];
char s[N];
ll gcd(ll x,ll y){
	if(!y) return x;
	else return gcd(y,x%y);
}
void get_border(ll n){
	ll ans=0;
	for(ll i=2;i<=n;i++){
		while(ans && s[ans+1]!=s[i]) ans=nex[ans];
		if(s[ans+1]==s[i]) ans++;
		nex[i]=ans;
	}
	
	while(ans){bor[++cnt]=n-ans;ans=nex[ans];}
	
	bor[++cnt]=n;
}
void rfs(ll len){
	ll siz=gcd(lim,len);
	for(ll i=0;i<lim;i++)res[i]=dis[i];
	for(ll i=0;i<len;i++)dis[i]=0x7f7f7f7f7f7f7f7f;
	for(ll i=0;i<lim;i++){
		ll tmp=res[i]%len;
		dis[tmp]=min(dis[tmp],res[i]);
	}
	for(ll i=0;i<siz;i++){
		top=0;Q[++top]=i;ll tmp=(i+lim)%len;
		while(tmp!=i){Q[++top]=tmp;tmp=(tmp+lim)%len;}
		for(ll j=top+1;j<=top*2;j++) Q[j]=Q[j-top];
		for(ll j=2;j<=(top<<1);j++) dis[Q[j]]=min(dis[Q[j]],dis[Q[j-1]]+lim);
	}
	lim=len;
	
}
void update(ll a,ll d,ll num){
	if(d<0) return;
	ll siz=gcd(lim,d);
	for(ll i=0;i<siz;i++){
		top=0;ll tmp=(i+d)%lim;Q[++top]=i;
		while(tmp!=i){Q[++top]=tmp;tmp=(tmp+d)%lim;}
		tmp=1;
		for(ll j=2;j<=top;j++) if(dis[Q[j]]<dis[Q[tmp]]) tmp=j;
		for(ll j=1;j<=top;j++) res[j]=Q[j];
		for(ll j=tmp;j<=top;j++) Q[j-tmp+1]=res[j];
		for(ll j=1;j<tmp;j++) {Q[j+(top-tmp+1)]=res[j];}
		ll head=1,tail=1;q2[1]=1;
		for(ll j=2;j<=top;j++){
			
			while(head<=tail && j-q2[head]>num) head++;
			
			dis[Q[j]]=min(dis[Q[j]],dis[Q[q2[head]]]+a+d*(j-q2[head]));
			
			while(tail>=head && dis[Q[q2[tail]]]+d*(j-q2[head])>dis[Q[j]]) tail--;
			q2[++tail]=j;
			
		}
	}
}
ll solve(ll n,ll w){
	ll L=1;
	while(L<=cnt){
		ll R=L;
		while(bor[R+1]-bor[R]==bor[L+1]-bor[L]) R++;
		rfs(bor[L]);
		if(R>cnt) {L=R;continue;}
		update(bor[L],(L==R?0:bor[L+1]-bor[L]),R-L);
		L=R;
	}
	ll ans=0;
	for(ll i=0;i<lim;i++){
		if(dis[i]>w) continue;
		ans+=(w-dis[i])/lim+1;
	}
	return ans;
}
int main(){
	ll t;
	cin>>t;
	while(t--){
		ll n,w;
		cin>>n>>w>>(s+1);if(w<n){cout<<0<<endl;continue;}cnt=0;w-=n;
		lim=n;dis[0]=0;
		get_border(n);
		cout<<solve(n,w)<<endl;
		for(ll i=0;i<n;i++) dis[i]=0x7f7f7f7f7f7f7f7f;
	}
}

标签:题解,ll,WC2016,P4156,字符串,include,Border,短路,dis
From: https://www.cnblogs.com/eastcloud/p/17272391.html

相关文章

  • CF1295E Permutation Separation 题解 线段树优化dp
    题目链接:https://codeforces.com/problemset/problem/1295/E题目大意:将排列\(p_1,p_2,\ldots,p_n\)先分成\(p_1,\ldots,p_k\)与\(p_{k+1},\ldots,p_n\)两个......
  • 【题解】[HEOI2013]SAO
    题目分析:考虑这是一个树形图,所以就先直接当作树来做。这个题其实就是让我们求解有多少种拓扑序而且题目中边方向的限制其实就是在限制拓扑序的前后,而一般这种题在设计\(......
  • 【题解】Codeforces Round 861(CF1808)A - E1
    我忘记了今天有阳间CF,所以就开打的很晚,所以只是说一下做法,代码实现....还是算了吧。但是我也看了,我的思路其他的人都有写,所以这个做法正确性没问题。A.LuckyNumbers题......
  • CF1009F 题解
    一、题目描述:给定一棵以 1 为根,n 个节点的树。设 d(u,x) 为 u的子树中到 u 距离为 x 的节点数。对于每个点,求一个最小的 k,使得 d(u,k) 最大。 二、......
  • CentOS7中远程连接数据库连不上的问题解决方法
      当远程连接数据库连接不起来时:可能原因:1.检查网络防火墙或其他安全设置是否阻止了连接  2.mysql服务是否启动,查看systemctlstatusmysql3.是否提前授权:......
  • ABC291题解(D-G)
    ABC291D-FlipCardsSolution:考虑DP,定义状态\(F_{i,0}\)为第\(i\)张卡片正面朝上的方案数,\(F_{i,1}\)为第\(i\)张卡片背面朝上的方案数,每次check是否相同然后转移即可......
  • 使用SQLALCHEMY 出现warning的问题解决
    运行程序时出现错误:UserWarning:NeitherSQLALCHEMY_DATABASE_URInorSQLALCHEMY_BINDSisset.DefaultingSQLALCHEMY_DATABASE_URIto"sqlite:///:memory:".'Neithe......
  • CF429D Tricky Function 题解 分治/平面最近点对
    题目链接:http://codeforces.com/problemset/problem/429/D题目大意:给定一个长度为\(n\)的数列\(a_1,a_2,\ldots,a_n\)。用\(s\)表示\(a\)的前缀和数组,即\(s_......
  • buu [CISCN] BadProgrammer题解
    [CISCN]BadProgrammer页面很长,有很多的按钮,但是点了之后都没反应查看源码、扫描打开到具体目录一个个目录点开看,在static/下找到了一个flag.ejs文件下载,打开......
  • [ARC131D] AtArcher 题解
    题意数轴上有一个箭靶以\(0\)为轴心左右对称,给定每个得分区域的范围和分值,要求射\(N\)支箭在靶上,且任意两支箭的距离不少于\(D\),求最大得分。保证从中心向两侧分数不......