【做题记录】HZOJ 多校-数论/多校-字符串/多校-图论Ⅱ
14. 图论 E. Minimum Path
考虑这个路径权值的实质,就是将最大值不算,最小值算两次。我们直接将其拓展,改成任选一条边不算,一条边算两次,由贪心可知显然不会改变答案。于是将每个点拆成四个点,跑 dijkstra 即可。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pb push_back
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,dis[maxn][4];
bool vis[maxn][4];
vector<pii> e[maxn];
priority_queue<pair<int,pii>> q;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
e[u].pb(mp(v,w));
e[v].pb(mp(u,w));
}
memset(dis,0x3f,sizeof(dis));
dis[1][0]=0,q.push(mp(0,mp(1,0)));
while(q.size()){
int u=q.top().sec.fir,s=q.top().sec.sec;
q.pop();
if(vis[u][s]){
continue;
}
vis[u][s]=1;
for(pii i:e[u]){
int v=i.fir,w=i.sec;
if(!vis[v][s]&&dis[v][s]>dis[u][s]+w){
dis[v][s]=dis[u][s]+w;
q.push(mp(-dis[v][s],mp(v,s)));
}
if(!(s&1)&&!vis[v][s|1]&&dis[v][s|1]>dis[u][s]){
dis[v][s|1]=dis[u][s];
q.push(mp(-dis[v][s|1],mp(v,s|1)));
}
if(!(s&2)&&!vis[v][s|2]&&dis[v][s|2]>dis[u][s]+2*w){
dis[v][s|2]=dis[u][s]+2*w;
q.push(mp(-dis[v][s|2],mp(v,s|2)));
}
if(!s&&!vis[v][3]&&dis[v][3]>dis[u][s]+w){
dis[v][3]=dis[u][s]+w;
q.push(mp(-dis[v][3],mp(v,3)));
}
}
}
for(int i=2;i<=n;i++){
cout<<dis[i][3]<<' ';
}
return 0;
}
}
signed main(){return asbt::main();}
15. 字符串 C. Prefix Function Queries
每次直接暴力求前缀数组是不可行的,因为 kmp 求前缀数组的时间复杂度是均摊 \(O(1)\)。考虑求前缀数组 \(fail_i\) 的过程,如果类似 ACAM 建一个 \(fail\) 树,我们实际上是从 \(i-1\) 开始在树上找到第一个 \(s_{j+1}=s_i\) 的位置。于是我们考虑建一个自动机(kmp 自动机),维护 \(nxt_{i,k}\) 表示 \(i\) 在 \(fail\) 树的根链上的第一个 \(s_{k+1}=s_{i+1}\) 的位置 \(k\),于是有 \(fail_i=nxt_{fail_{i-1},s_i}+1\)。
考虑 \(nxt\) 怎么维护,类似 ACAM 的思路,有 \(nxt_{i,j}=\begin{cases}\begin{aligned}&i&j=s_{i+1}\\&nxt_{fail_i,j}&j\ne s_{i+1}\end{aligned}\end{cases}\)。于是时间复杂度 \(O(26(|s|+\sum|t|))\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+39;
int n,m,fail[maxn],nxt[maxn][26];
char s[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>(s+1)>>m;
n=strlen(s+1);
for(int i=2,j=0;i<=n;i++){
while(j&&s[j+1]!=s[i]){
j=fail[j];
}
if(s[j+1]==s[i]){
j++;
}
fail[i]=j;
}
for(int i=0;i<n;i++){
for(int j=0;j<=25;j++){
nxt[i][j]=nxt[fail[i]][j];
}
nxt[i][s[i+1]-'a']=i;
}
while(m--){
cin>>(s+n+1);
int k=strlen(s+n+1);
for(int i=n,j=fail[n];i<n+k;i++){
for(int j=0;j<=25;j++){
nxt[i][j]=nxt[fail[i]][j];
}
nxt[i][s[i+1]-'a']=i;
j=nxt[j][s[i+1]-'a'];
if(s[j+1]==s[i+1]){
j++;
}
fail[i+1]=j;
cout<<j<<' ';
}
cout<<'\n';
}
return 0;
}
}
signed main(){return asbt::main();}
17. 字符串 F. 【模板】AC 自动机
什么玩意儿也能进题单了。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,tot,tr[maxn][26],fail[maxn],q[maxn],end[maxn],ans[maxn],deg[maxn];
string s;
il void build(){
int hd=1,tl=0;
for(int i=0;i<=25;i++){
if(tr[0][i]){
q[++tl]=tr[0][i],deg[0]++;
}
}
while(hd<=tl){
int u=q[hd++];
for(int i=0;i<=25;i++){
if(tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
deg[tr[fail[u]][i]]++;
q[++tl]=tr[u][i];
}else{
tr[u][i]=tr[fail[u]][i];
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>s;
int p=0;
for(char i:s){
int d=i-'a';
if(!tr[p][d]){
tr[p][d]=++tot;
}
p=tr[p][d];
}
end[i]=p;
}
build();
cin>>s;
int p=0;
for(char i:s){
p=tr[p][i-'a'];
ans[p]++;
}
int hd=1,tl=0;
for(int i=0;i<=tot;i++){
if(!deg[i]){
q[++tl]=i;
}
}
while(hd<=tl){
int u=q[hd++];
ans[fail[u]]+=ans[u];
if(--deg[fail[u]]==0){
q[++tl]=fail[u];
}
}
for(int i=1;i<=n;i++){
cout<<ans[end[i]]<<'\n';
}
return 0;
}
}
signed main(){return asbt::main();}
18. 图论 F. [GXOI/GZOI2019] 旅行者
直接二进制分组即可,时间复杂度线性对数方。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace asbt{
const int maxn=1e5+5;
const ll inf=1e18;
int T,n,m,kk,a[maxn];
ll dis[maxn];
bool vis[maxn];
vector<pii> e[maxn];
priority_queue<pii> q;
il void dijkstra(){
while(q.size()){
int u=q.top().sec;
q.pop();
if(vis[u]){
continue;
}
vis[u]=1;
for(pii i:e[u]){
int v=i.fir,w=i.sec;
if(!vis[v]&&dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push(mp(-dis[v],v));
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n>>m>>kk;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
e[u].pb(mp(v,w));
}
for(int i=1;i<=kk;i++){
cin>>a[i];
}
ll ans=inf;
for(int i=0;i<=16;i++){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
for(int j=1;j<=kk;j++){
if(j>>i&1){
dis[a[j]]=0,q.push(mp(0,a[j]));
}
}
dijkstra();
for(int j=1;j<=kk;j++){
if(!(j>>i&1)){
ans=min(ans,dis[a[j]]);
}
}
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
for(int j=1;j<=kk;j++){
if(!(j>>i&1)){
dis[a[j]]=0,q.push(mp(0,a[j]));
}
}
dijkstra();
for(int j=1;j<=kk;j++){
if(j>>i&1){
ans=min(ans,dis[a[j]]);
}
}
}
cout<<ans<<'\n';
for(int i=1;i<=n;i++){
e[i].clear();
}
}
return 0;
}
}
signed main(){return asbt::main();}
19. 数论 U. [abc322_g]Two Kinds of Base
记 \(F(S,a,b)=f(S,a)-f(S,b)=\sum_{i=1}^{k}S_i\times(a^{k-i}-b^{k-i})\),因为 \((a-b)|(a^k-b^k)\),有 \((a-b)|F(S,a,b)\)。由于 \(F(S,a,b)\) 关于 \(k\) 是指数级的,因此 \(k\) 非常小,不会超过 \(18\)。
注意到当 \(k\ge3\) 时,有限制 \(a^2-b^2\le X\),此时合法的 \((a,b)\) 也非常少,大概是 \(O(X\log X)\) 级别。
Proof:令 \(s=a-b\),则 \(s|F(S,a,b)\)。由 \(a^2-b^2\le X\) 有 \(s^2+2bs\le X\),故对于每个 \(s\),\(b\) 最多有 \(\frac{X}{s}\) 个,这是一个调和级数。
于是我们可以枚举 \(k\) 和 \(a-b\),然后再枚举出合法的 \((a,b)\),对 \(S\) 进行计数。实际上由于 \(a^k-b^k>(b-1)\sum_{i=0}^{k-1}a^i-b^i\)(可以归纳证明),\(S\) 如果存在则前 \(k-1\) 位是唯一确定的,从大到小贪心地减去 \(a^{k-i}-b^{k-i}\) 即可,时间复杂度 \(O(k)\),也就是 \(O(\log X)\)。因此这一部分的总时间复杂度大概是线性对数方级别。
但是还有一种情况,即 \(k=2\)。此时我们发现,\(F(S,a,b)=S_1\times(a-b)\)。不妨枚举 \(S_1\),则 \(a-b=\frac{X}{S_1}\),故 \(b\in(S_1,N-\frac{X}{i}]\),对于 \(<10\) 的 \(b\) 单独计算 \(S_2\) 的方案数,对于 \(\ge10\) 的 \(b\) \(S_2\) 有 \(10\) 种填法,\(O(1)\) 计算即可。
总时间复杂度 \(O(X\log^2X)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int mod=998244353;
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il void add(int &x,int y){
x=pls(x,y);
}
il int mns(int x,int y){
return x<y?x-y+mod:x-y;
}
il void sub(int &x,int y){
x=mns(x,y);
}
int n,m;
il bool check(int a,int b,int k){
int x=m;
for(int i=k-1;i;i--){
int t=pow(a,i)-pow(b,i);
if(x/t>min(9,b-1)){
return 0;
}
x%=t;
}
return !x;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
int ans=0;
for(int i=1;i<=9;i++){
if(m%i){
continue;
}
for(int j=i+1;j<=9;j++){
if(j>n-m/i){
break;
}
add(ans,j);
}
ans=(ans+max(0,n-m/i-9)*10ll)%mod;
}
for(int i=1;i<=m;i++){
// cout<<"i = "<<i<<'\n';
if(m%i){
continue;
}
for(int k=3;k<=18;k++){
// cout<<" k = "<<k<<'\n';
for(int a=i+2,b=2;a<=n&&pow(a,k-1)-pow(b,k-1)<=m;a++,b++){
// if(i==16&&k==17){
// cout<<a<<' '<<b<<' ';
// }
if(check(a,b,k)){
add(ans,min(b,10));
}
// if(i==16&&k==17){
// puts("666");
// }
}
}
}
cout<<ans;
return 0;
}
}
signed main(){return asbt::main();}
20. 数论 R. [NOI Online 2022 入门组] 数学游戏
首先如果 \(x\nmid z\) 则必然无解。
记 \(\gcd(x,y)=d,x=ad,y=bd\),则 \(z=abd^3\)。于是我们可以求出 \(bd^2=\frac{z}{x}\)。又 \(a\perp b\),可以得到 \(d^2=\gcd(x^2,\frac{z}{x})\)。于是 \(y=\frac{z}{x\sqrt{\gcd(x^2,\frac{z}{x})}}\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int T;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
ll x,z;
cin>>x>>z;
if(z%x){
cout<<-1<<'\n';
continue;
}
ll d2=__gcd(z/x,x*x);
ll d=sqrtl(d2);
if(d*d==d2){
cout<<z/x/d<<'\n';
}else{
cout<<-1<<'\n';
}
}
return 0;
}
}
signed main(){return asbt::main();}
21. 数论 I. [THUPC2019] 令人难以忘记的题目名称
过于复杂,另写了个题解。
22. 图论 L. Blood Cousins Return
离线,用线段树维护子树内每个深度有哪些儿子(给线段树叶子节点开个 set),线段树合并(叶子节点启发式合并)即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,a[maxn],fa[maxn],ans[maxn];
int rt[maxn],tot,ls[maxn*19],rs[maxn*19];
string s;
vector<int> e[maxn];
vector<pii> q[maxn];
set<int> st[maxn*19];
map<string,int> id;
il void insert(int &id,int l,int r,int p,int x){
if(!id){
id=++tot;
}
if(l==r){
st[id].insert(x);
return ;
}
int mid=(l+r)>>1;
if(p<=mid){
insert(ls[id],l,mid,p,x);
}else{
insert(rs[id],mid+1,r,p,x);
}
}
il int merge(int p,int q,int l,int r){
if(!p||!q){
return p+q;
}
if(l==r){
if(st[p].size()<st[q].size()){
swap(st[p],st[q]);
}
for(int x:st[q]){
st[p].insert(x);
}
return p;
}
int mid=(l+r)>>1;
ls[p]=merge(ls[p],ls[q],l,mid);
rs[p]=merge(rs[p],rs[q],mid+1,r);
return p;
}
il int query(int id,int l,int r,int p){
if(!id){
return 0;
}
if(l==r){
// for(int x:st[id]){
// cout<<x<<' ';
// }
// cout<<'\n';
return st[id].size();
}
int mid=(l+r)>>1;
if(p<=mid){
return query(ls[id],l,mid,p);
}else{
return query(rs[id],mid+1,r,p);
}
}
il void dfs(int u,int d){
for(int v:e[u]){
dfs(v,d+1);
rt[u]=merge(rt[u],rt[v],1,n);
}
insert(rt[u],1,n,d,a[u]);
for(pii i:q[u]){
// cout<<u<<' '<<i.fir<<":\n";
if(d+i.fir<=n){
ans[i.sec]=query(rt[u],1,n,d+i.fir);
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1,x,cnt=0;i<=n;i++){
cin>>s>>x;
if(!id.count(s)){
id[s]=++cnt;
}
a[i]=id[s],fa[i]=x;
if(x){
e[x].pb(i);
}
// cout<<a[i]<<' ';
}
// cout<<'\n';
cin>>m;
for(int i=1,v,k;i<=m;i++){
cin>>v>>k;
q[v].pb(mp(k,i));
}
for(int i=1;i<=n;i++){
if(!fa[i]){
dfs(i,1);
}
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
23. 字符串 B. Prefixes and Suffixes
跑出 \(nxt\) 数组,在 \(nxt\) 树上做个前缀和即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,nxt[maxn],cnt[maxn];
string s;
vector<pii> ans;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>s;
n=s.size(),s=" "+s;
for(int i=2,j=0;i<=n;i++){
while(j&&s[j+1]!=s[i]){
j=nxt[j];
}
if(s[j+1]==s[i]){
j++;
}
nxt[i]=j;
}
for(int i=n;i;i--){
cnt[i]++,cnt[nxt[i]]+=cnt[i];
}
for(int i=n;i;i=nxt[i]){
ans.pb(mp(i,cnt[i]));
}
cout<<ans.size()<<'\n';
sort(ans.begin(),ans.end());
for(pii i:ans){
cout<<i.fir<<' '<<i.sec<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号