题解:P10646 [NordicOI 2023] ChatNOI
思路
何意味?
发现字符串没有任何用,可以将每个字符串映射到整数上。
考虑图论建模,将每个长度为
那么在一个长度为
先考虑考虑如何判断答案是否大于等于
这个就十分经典了,考虑倒着建图,然后跑拓扑排序即可。若跑完后一个点的度数不是
若从大到小枚举
为了构造方案,我们还需要为每个点找到最长路上的后继。对于可以到环的点,至少有一条满足终点仍可以到环的出边,后继为这条出边的终点。对于其他点,后继就是图上最长路的后继。
发现边权之和是
其实还可以做到更优。
进一步挖掘性质,若
使用哈希表将每个字符串映射为一个数以及将一个连续段映射到一个点可以做到时间复杂度
代码
#include<bits/stdc++.h>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define int long long
using namespace std;
using namespace __gnu_pbds;
const int N = 5e5+5,base1 = 23333,mod1 = 1e9+9,base2 = 20101013,mod2 = 147744151,base = 1e9,inf = 2e9;
int n,k,q,tot,cnt,a[N],en[N],lim[N];
string t[N],s;
gp_hash_table<int,int> mp,mp1,mp2;
vector<pair<int,int>> vec[N],vec2[N];
int now[N];
int head[N],to[N],nxt[N],top;
inline void add(int u,int v)
{
nxt[++top] = head[u];
head[u] = top;
to[top] = v;
}
int p[N],ttt;
bool vis[N];
int rt[N],dis[N],du[N];
vector<string> ans[N];
bool ok[N];
inline void work(int x)
{
while(now[x]<vec2[x].size()&&vec2[x][now[x]].first<=dis[x])
{
auto _ = vec2[x][now[x]++];
int lim = _.first,id = _.second,p = x;
while(lim--)
{
p = rt[p];
ans[id].push_back(t[en[p]]);
}
}
}
inline int get(string &s)
{
int res = 0;
for(auto i:s) res = res*27+i-'a'+1;
return res;
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i = 1;i<=n;i++)
{
cin>>s;
int x = get(s);
if(!mp[x]) mp[x] = ++tot,t[tot] = s;
a[i] = mp[x];
}
int las = 0;
for(int i = 1;i<=n-k+1;i++)
{
int h1 = 0,h2 = 0;
for(int j = i;j<i+k;j++)
h1 = (h1*base1+a[j])%mod1,h2 = (h2*base2+a[j])%mod2;
int h = h2*mod1+h1;
if(!mp1[h])
{
mp1[h] = ++cnt;
en[cnt] = a[i+k-1];
}
int now = mp1[h];
if(las) mp2[las*base+now]++;
las = now;
}
for(auto _:mp2)
vec[_.second].push_back({_.first/base,_.first%base});
cin>>q;
for(int i = 1,x;i<=q;i++)
{
cin>>x;
int h1 = 0,h2 = 0;
for(int j = 1;j<=k;j++)
{
cin>>s;
int now = mp[get(s)];
h1 = (h1*base1+now)%mod1,h2 = (h2*base2+now)%mod2;
}
int p = mp1[h2*mod1+h1];
vec2[p].push_back({x,i}),lim[i] = x;
}
for(int i = 1;i<=cnt;i++) sort(vec2[i].begin(),vec2[i].end());
dis[0] = -1;
for(int i = n;i;i--)
{
if(!vec[i].size()) continue;
for(auto _:vec[i])
{
if(!vis[_.second]) p[++ttt] = _.second,vis[_.second] = 1;
if(!vis[_.first]) p[++ttt] = _.first,vis[_.first] = 1;
add(_.second,_.first);
}
for(int i = 1;i<=ttt;i++) du[p[i]] = 0;
for(int i = 1;i<=ttt;i++)
{
int x = p[i];
dis[x] = 0;
for(int i = head[x];i;i = nxt[i])
{
int y = to[i];
du[y]++;
}
}
queue<int> q;
for(int i = 1;i<=ttt;i++)
if(!du[p[i]]) q.push(p[i]);
while(!q.empty())
{
int u = q.front();q.pop();
for(int i = head[u];i;i = nxt[i])
{
int v = to[i];
if(dis[u]+1>dis[v]) dis[v] = dis[u]+1,rt[v] = u;
du[v]--;
if(!du[v]) q.push(v);
}
}
for(int i = 1;i<=ttt;i++)
{
int x = p[i];
if(du[x])
{
dis[x] = 2e9;
for(int i = head[x];i;i = nxt[i])
{
int y = to[i];
rt[y] = x;
}
}
}
for(int i = 1;i<=ttt;i++)
work(p[i]);
}
for(int i = 1;i<=q;i++)
{
if(!ans[i].size()) while(lim[i]--) cout<<t[1]<<' ';
else for(auto _:ans[i]) cout<<_<<' ';
cout<<'\n';
}
return 0;
}