牛客练习赛61(D dij E hash F 动态开点线段树+点分树)

之所以现在才写题解,是因为这个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);
        }
    }
}
发布了536 篇原创文章 · 获赞 71 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/105544845