之所以现在才写题解,是因为这个F题太难补了,学了波点分治和点分树才能补呀
D-最短路变短了
题意:
做法:正反跑一边dij,判断一下方向边时 最短路是否改变即可。加了建立最短路树然后判桥。。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
const int N=2e5+10;
LL n,m,ans;
LL d1[N],d2[N],x[N],y[N],z[N];
LL cnt=1,head[N],to[N<<1],nxt[N<<1],len[N<<1];
void add_edge(LL x,LL y,LL z) {
nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt;
}
bool vis[N];
priority_queue<pii> q;
void Dijkstra(LL d[],LL s) {
while (!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
d[s]=0; q.push(make_pair(0,s));
while (!q.empty()) {
pii x=q.top(); q.pop();
if (vis[x.second]) continue;
vis[x.second]=1;
for (LL i=head[x.second];i;i=nxt[i]) {
LL y=to[i];
if (d[y]>d[x.second]+len[i]) {
d[y]=d[x.second]+len[i];
q.push(make_pair(-d[y],y));
}
}
}
}
int num,low[N],dfn[N],bridge[N];
void tarjan(int x,int in) {
dfn[x]=low[x]=++num;
for (int i=head[x];i;i=nxt[i]) {
int y=to[i];
if (!dfn[y]) {
tarjan(y,i);
low[x]=min(low[x],low[y]);
if (low[y]>dfn[x])
bridge[len[i]]=bridge[len[i^1]]=1;
} else if (i!=(in^1))
low[x]=min(low[x],dfn[y]);
}
}
void getbridge() {
cnt=1; memset(head,0,sizeof(head));
for (int i=1;i<=m;i++)
if (d1[x[i]]+z[i]+d2[y[i]]==ans)
add_edge(x[i],y[i],i),add_edge(y[i],x[i],i);//最短路树建新图
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i,0);
}
int main()
{
cin>>n>>m;
for (int i=1;i<=m;i++) scanf("%lld%lld%lld",&x[i],&y[i],&z[i]);
memset(d1,0x3f,sizeof(d1)); memset(d2,0x3f,sizeof(d2));
for (int i=1;i<=m;i++) add_edge(x[i],y[i],z[i]);
Dijkstra(d1,1);
cnt=1; memset(head,0,sizeof(head));
for (int i=1;i<=m;i++) add_edge(y[i],x[i],z[i]);
Dijkstra(d2,n);
ans=d1[n];
getbridge();
int q;cin>>q;
while(q--)
{
int id;
scanf("%d",&id);
if (ans>d1[y[id]]+d2[x[id]]+z[id]) puts("YES");
else if (bridge[id]) puts("NO");//是桥 代表最短路径上得一条
else puts("NO");
}
}
E-相似的子串
题意:
做法:Hash预处理,二分长度,然后O(N)遍历得到hash值最多的相同个数。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
ll h[2][N];
ll base[2]={29,31};
ll mod[2] = {1000000007,10000813};
ll f[2][N];
int n,k;
int getid(char c) {return c-'a'+1;}
char s[N];
void init()
{
f[1][0]=f[0][0]=f[2][0]=1;
for(int i=1;i<N;++i)
for(int j=0;j<2;++j)
f[j][i]=f[j][i-1]*base[j]%mod[j];///预处理base[j]进制的i次方
}
ll getvalue(int l,int r,int j)
{
return (h[j][r]-h[j][l-1]*f[j][r-l+1]%mod[j]+mod[j])%mod[j];
}
int run(int x)
{
map<pair<ll,ll>,int>mp;
map<pair<ll,ll>,int>pos;
int num=1;
for(int i=1;i+x-1<=n;++i)
{
int l=i,r=i+x-1;
ll tmp0=getvalue(l,r,0);
ll tmp1=getvalue(l,r,1);
pair<ll,ll>t=make_pair(tmp0,tmp1);
if(i>pos[t]) mp[t]++,pos[t]=r;
if(mp[t]>=k) return 1;
}
return 0;
}
int main()
{
init();
cin>>n>>k;
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;++i)
for(int j=0;j<=1;++j)
h[j][i]=(h[j][i-1]*base[j]%mod[j]+getid(s[i]))%mod[j];
int ans=0;
int l=1,r=n/k;
while(l<=r)
{
int mid=l+r>>1;
if(run(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
F-苹果树
题意:
点分治学习博客:博客链接
学了点分治怎么感觉还是不会写啊?看下官方题解:
核心思想:我们给每一个结点x动态开点一棵线段树,维护从xx到在点分树中x子树的所有苹果的最小距离
接着点分树是看别人代码学的怎么构造
代码参考来自:牛客提交记录
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
int n,m,a[N],rt,f[N],fa[N][40],dep[N],dis[N][40],sz[N],sum,tot,head[N],nex[N<<1],to[N<<1],wi[N<<1];
void add(int u,int v,int w){to[++tot]=v;nex[tot]=head[u];head[u]=tot;wi[tot]=w;}
bool vis[N];
int root[N],t[N*40],num,ls[N*40],rs[N*40];
void up(int l,int r,int x,int v,int &now)
{
if(!now) now=++num,t[now]=inf;
t[now]=min(t[now],v);
if(l==r) return;
int m=l+r>>1;
if(x<=m) up(l,m,x,v,ls[now]);
else up(m+1,r,x,v,rs[now]);
}
int query(int l,int r,int x,int y,int now)
{
if(!now||r<x||l>y) return inf;
if(l>=x&&r<=y) return t[now];
int m=l+r>>1;
return min(query(l,m,x,y,ls[now]),query(m+1,r,x,y,rs[now]));
}
void getroot(int u,int p)//getsize和getroot合并了
{
sz[u]=1;f[u]=0;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(vis[v]||v==p)continue;
getroot(v,u);
sz[u]+=sz[v];
f[u]=max(f[u],sz[v]);
}
f[u]=max(f[u],sum-sz[u]);
if(f[u]<f[rt]) rt=u;
}
void solve(int u,int p,int rt,int d)//点分树详细过程
{
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(vis[v]||v==p) continue;
fa[v][++dep[v]]=rt;//v节点 dep[v]级重心得父亲,相当于重新建图
dis[v][dep[v]]=d+wi[i];//预处理 替代了lca
solve(v,u,rt,d+wi[i]);
}
}
void build(int u)//建立点分树
{
vis[u]=true;
solve(u,0,u,0);
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(vis[v])continue;
rt=0;
sum=sz[v];
getroot(v,0);
build(rt);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
f[0]=1e9;rt=0;sum=n;
getroot(1,0);
build(rt);
for(int i=1;i<=n;i++) fa[i][dep[i]+1]=i;//等于自己
for(int i=1;i<=n;i++)
{
for(int j=dep[i]+1;j;j--)
up(1,10000,a[i],dis[i][j],root[fa[i][j]]);
}
while(m--)
{
int opt;scanf("%d",&opt);
if(opt==1)
{
int u,x;scanf("%d%d",&u,&x);
for(int i=dep[u]+1;i;i--)
up(1,10000,x,dis[u][i],root[fa[u][i]]);
}
else
{
int u,x,y;scanf("%d%d%d",&u,&x,&y);
int ans=inf;
for(int i=dep[u]+1;i;i--)
{
int mn=query(1,10000,x,y,root[fa[u][i]]);
ans=min(ans,mn+dis[u][i]);
}
if(ans==inf) printf("-1\n");
else printf("%d\n",ans*2);
}
}
}