首页 > 其他分享 >题解:【COCI2019-2020#6】 Trener

题解:【COCI2019-2020#6】 Trener

时间:2023-03-25 10:13:07浏览次数:51  
标签:cnt const int 题解 Trener 2020 MAX now mod

题目链接

本人于三月二十四日模拟赛本题中使用 \(\mathcal O(n^2 k + n k^2)\) 哈希+DP,因神秘常数原因竟打不过 \(\mathcal O(n^2 k^2)\),甚至被卡的TLE飞起,怒挂五十分。赛后交了一页的TLE,最后换成自然溢出才能过,铭记贰点贰叁

不会吧不会吧不会还有人这个题写 \(\mathcal O(n^2 k + n k^2)\) 吧。/kel

平凡的做法其他题解说的很清楚了,注意到我们 \(O(k^2)\) 的判断前后缀是否相等其实没有必要,这里可以将哈希值从小到大排序并记下编号,使用双指针优化只扫一遍,将前后缀相同的记录在一起,然后再去重累加上答案,时间复杂度 \(\mathcal O(n^2 k + n(k + n \log k))\),瓶颈在于排序。

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define mp make_pair
#define pb push_back
using namespace std;

bool Mbe;

namespace LgxTpre
{
	static const int MAX=1510;
	static const int INF=4557430888798830399;
	static const int mod=1e9+7;
	static const int bas=151;
	
	inline void Madd(int &a,int b) {a=a+b>=mod?a+b-mod:a+b;}
	inline void Mdel(int &a,int b) {a=a-b<0?a-b+mod:a-b;}
	inline int  Cadd(int a,int b)  {return a+b>=mod?a+b-mod:a+b;}
	
	int n,k,ans,len;
	string s;
	ull suf[MAX][MAX],pre[MAX][MAX],all[MAX][MAX],tmp1[MAX],tmp2[MAX];
	int dp[MAX][MAX],vis[MAX],id[MAX],idd[MAX],cas[MAX],num;
	vector<int> buc[MAX];
		
	inline void lmy_forever()
	{
		cin>>n>>k;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=k;++j)
			{
				cin>>s; len=s.size();
				for(int h=0;h<len;++h) 
				{
					all[i][j]=all[i][j]*bas+s[h];
					if(h!=0) pre[i][j]=pre[i][j]*bas+s[h];
					if(h!=len-1) suf[i][j]=suf[i][j]*bas+s[h];
				}
			}
		for(int i=1;i<=k;++i) dp[1][i]=1;
		for(int i=1;i<n;++i)
		{
			for(int j=1;j<=k;++j) buc[j].clear(),id[j]=idd[j]=j;
			sort(idd+1,idd+k+1,[i](int a,int b){return all[i][a]<all[i][b];});
			for(int j=1;j<=k;++j) tmp1[j]=all[i][idd[j]];
			sort(id+1,id+k+1,[i](int a,int b){return pre[i+1][a]<pre[i+1][b];});
			for(int j=1;j<=k;++j) tmp2[j]=pre[i+1][id[j]];
			for(int j=1,iter=1;j<=k;++j)
			{
				while(iter<=k&&tmp2[iter]<tmp1[j]) ++iter;
				for(int con=iter;con<=k&&tmp2[con]==tmp1[j];++con) buc[idd[j]].pb(id[con]); 
			}
			sort(id+1,id+k+1,[i](int a,int b){return suf[i+1][a]<suf[i+1][b];});
			for(int j=1;j<=k;++j) tmp2[j]=suf[i+1][id[j]];
			for(int j=1,iter=1;j<=k;++j)
			{
				while(iter<=k&&tmp2[iter]<tmp1[j]) ++iter;
				for(int con=iter;con<=k&&tmp2[con]==tmp1[j];++con) buc[idd[j]].pb(id[con]); 
			}
			for(int j=1;j<=k;++j)
			{
				num=0;
				for(auto it:buc[j]) if(!vis[it]) vis[it]=1,cas[++num]=it;
				buc[j].clear();
				for(int p=1;p<=num;++p) vis[cas[p]]=0,buc[j].pb(cas[p]);
			}
			for(int j=1;j<=k;++j)
				if(dp[i][j])
					for(auto it:buc[j])
						Madd(dp[i+1][it],dp[i][j]);	
		}
		for(int i=1;i<=k;++i) Madd(ans,dp[n][i]);
		cout<<ans<<endl;
		return;
	}
}

bool Med;

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0);
	fprintf(stderr,"%.3lf MB\n",(&Med-&Mbe)/1048576.0);
	LgxTpre::lmy_forever();
	cerr<<1e3*clock()/CLOCKS_PER_SEC<<" ms\n";
	return (0-0);
}

事实上我们也可以不使用哈希。对于这种多个字符串记录相同前后缀数量的问题,我们更容易想到使用 \(\text{Trie}\) 树操作。我们记以当前节点的答案数量为 \(cnt_i\),依次将每个字符串插入 \(\text{Trie}\) 树,然后直接在 \(\text{Trie}\) 树中查找其前后缀的位置并累加 \(cnt\) 的值。这里注意当查找前后缀所在的节点最后相同时,说明出现了重复,我们只累加一遍。由于在 \(\text{Trie}\) 树上多个串共用一个节点位置,所以我们最后统计的答案实则为累加一个串前后缀 \(cnt\) 操作的增量。时间复杂度 \(\mathcal O(n^2 k)\),瓶颈在于读入。/jk

#include<bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
using namespace std;

bool Mbe;

namespace LgxTpre
{
	static const int MAX=1510*1510;
	static const int INF=4557430888798830399;
	static const int mod=1e9+7;
	
	inline void Madd(int &a,int b) {a=a+b>=mod?a+b-mod:a+b;}
	inline void Mdel(int &a,int b) {a=a-b<0?a-b+mod:a-b;}
	inline int  Cadd(int a,int b)  {return a+b>=mod?a+b-mod:a+b;}
	
	int n,k,ans;
	int now,pre,suf;
	
	string s;
	int ch[MAX][30],cnt[MAX],tot=1;
	inline int insert()
	{
		int len=s.size(),now=1;
		for(int i=0;i<len;++i)
		{
			int to=s[i]-'a';
			if(!ch[now][to]) ch[now][to]=++tot;
			now=ch[now][to];
		}
		return now;
	}
	inline int find(int cas)
	{
		int len=s.size(),now=1;
		for(int i=cas;i<len-(1-cas);++i)
		{
			int to=s[i]-'a';
			if(!ch[now][to]) return 0;
			now=ch[now][to];
		}
		return now;
	}
		
	inline void lmy_forever()
	{
		cin>>n>>k; cnt[1]=1;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=k;++j)
			{
				cin>>s;
				now=insert();
				pre=find(0);
				suf=find(1);
				if(i==n) Mdel(ans,cnt[now]);
				if(pre==suf) Madd(cnt[now],cnt[pre]);
				else Madd(cnt[now],Cadd(cnt[pre],cnt[suf]));
				if(i==n) Madd(ans,cnt[now]);
			}
		cout<<ans<<endl;
		return;
	}
}

bool Med;

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0);
	fprintf(stderr,"%.3lf MB\n",(&Med-&Mbe)/1048576.0);
	LgxTpre::lmy_forever();
	cerr<<1e3*clock()/CLOCKS_PER_SEC<<" ms\n";
	return (0-0);
}

最后放一张评测图片,高下立判:

7_OPBX__X_`R_H_RJX@W2_E.png

标签:cnt,const,int,题解,Trener,2020,MAX,now,mod
From: https://www.cnblogs.com/LittleTwoawa/p/17254190.html

相关文章

  • 训练round1题解
    SMUSpring2023TrialContestRound1A.大意:给出一个仅由0,1组成的字符串,该字符串是多次在首位各加0或1得到,问最短的原始字符串的长度。思路:一次操作增加两个字符,特......
  • [ABC276G] Count Sequences 题解
    考虑差分,设\(d_i=a_i-a_{i-1}\),特别的,\(d_1=a_1\),那么约束就变成了\(\displaystyle\sumd_i\lem\)。对所有\(i>1\)有\(d_i\not\equiv0\pmod3\)。发现\(d_1\)......
  • CSP20230319-4 星际网络II 题解
    〇、题目题目描述随着星际网络的进一步建设和规模的增大,一个新的问题出现在网络工程师面前——地址空间不够用了!原来,星际网络采用了传统的IPv6协议,虽然有\(2^{128}\)级......
  • 【sklearn版本问题解决】
    一、报错fromsklearn.utils.validationimportcheck_memoryImportError:cannotimportname'check_memory'二、解决1.首先我去看了相关位置的源码发现validation.py里......
  • Windows 10 1909 (Updated 2020-01-23)
    Windows10商业版(含教育版、企业版、专业版、专业教育版、专业工作站版)SHA1:67F9C7D6EC42CB1257697516F134799E020DE8E3ed2k://|file|cn_windows_10_business_editions_......
  • Windows 10 2004 (Updated 2020-05-12 v19041.208)
    Windows10商业版(含教育版、企业版、专业版、专业教育版、专业工作站版)SHA1:ED65CC6F3B4F90FDBDAB949BA6286708E8DCF0F1ed2k://|file|cn_windows_10_business_editions_......
  • Windows 10 2004 (Updated 2020-07-24 v19041.388)
    Windows10商业版(含教育版、企业版、专业版、专业教育版、专业工作站版)SHA1:77C83FF5329A5685649DA8A2D0936C045A0B25CAed2k://|file|cn_windows_10_business_editions_......
  • 关于安装google-colab包速缓慢的问题解决
    最近想从colab上重构源码包在本地实现,但是总有一个包是来自google.colab的fromgoole.colabimportfiles提示没有google.colab的安装模块,需要安装google-colab的这个包......
  • T324159 卡空间的题目/电脑白吃 题解
    https://www.luogu.com.cn/problem/T324159题目大意:给定一个大小为\(n\)的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于\(\lfloor\frac{n}{2}\rfloo......
  • CF1168C And Reachability 题解 线性dp
    题目链接https://codeforces.com/problemset/problem/1168/C题目大意给定一个数组\(a\),从下标\(x\)能够转移到下标\(y\)要满足\(x\lty\)且\(a_{p_i}\,\&\,a......