JZOJ2017.08.20B组

59 篇文章 0 订阅

T1

Description

邪恶的707刚刚从白垩纪穿越回来,心中产生了一个念头:我要统治人类!
但是统治人类是很庞大且复杂的一个工程,707尝试了洗脑,催眠,以及武装镇压都没能成功地统治人类,于是她决定从科学上对人类的基因进行研究从而达到他的目的。
707获取了人类的基因信息并尝试对基因进行实验。他发现可以把人类的基因看做一个只包含小写字母的字符串,并定义从头开始任意长度的基因为“源头基因”人类身上与源头基因完全匹配的片段越多,这个人就越容易被控制。于是707就开始了他邪恶的计划……
作为人类卫士的射手ZMiG自然不会让707得逞,他决定拯救人类,现在他拿到了其中一个人被改造后的基因,他想请你统计一下它的基因中究竟有多少基因片段是可以与源头基因相匹配的

Input

输入一个只包含小写字母的字符串S

Output

输出一个整数,代表可以与源头基因相匹配的基因片段数量。

思路:

kmp大法!!!
其实就是一种匹配优化
我们定义up[i]为最近的上一次出现i的位置,times[i]为以i结尾的字符串出现的次数
再打一个KMP,就可以过了

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,i,j,k,up[1000005],times[1000005];
char a[1000005];
long long ans;
int main()
{
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);
    scanf("%s",a+1);
    n=strlen(a+1);
    times[1]=1;
    j=0;
    for (i=2;i<=n;i++)
    {
        while (j&&a[j+1]!=a[i]) j=up[j];
        if (a[j+1]==a[i]) j++;
        up[i]=j;
        times[i]=times[j]+1;
        ans+=times[i]-1;
    }
    printf("%lld\n",ans+n);
}

T2
Description

在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。
但是,组成联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径。
为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

Input

第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。这些太空隧道按照输入的顺序依次建成。

Output

输出共P行。如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出”No”(不含引号)。
Description

在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。
但是,组成联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径。
为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

Input

第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。这些太空隧道按照输入的顺序依次建成。

Output

输出共P行。如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出”No”(不含引号)。

思路:

首先我们先要知道一个东西:在一棵树上随便链接两个点都会形成一个环
那么我们就可以建一棵树来做,这样就不用每次暴力求出这个是否为强连通分量
那么我们按顺序连边,如果当前连进来的边会形成一个环,我们就暂时不加,先记录下来。(造树可以用BFS实现,而且要求出每一个点的深度)
对于给定的两个节点,我们可以先找到其最近公共祖先lca,再将lca到这两个点的路径上所有的点合并为同一个强连通分量。这里可以用并查集维护。
初始时每个节点指向自己,在合并时,将指针指向所合并的lca。(也就是把环压成一个点)
接下来的问题就是如何找到两个点的最近公共祖先呢?最近公共祖先有一个特点:它在树中的高度H不大于所要查询的那两个节点在树中的高度。我们可以迭代完成查询lca的过程。
例如:给定两个点X,Y,我们得到这两个点在树中的高度,接下来如果Hx>=Hy,我们就将
X替换为它的父亲(这里的父亲指缩点后的父亲),否则就将Y替换为它的父亲,直到X=Y为止。
这期间,我们可以顺便完成并查集的合并操作。

代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int first1[400010],next1[400010],last1[200010],num=0,tot=0,f[200010],deep[200010],sz[200010],fa[200010],k[200010];
bool bz[200010];
struct node
{
    int x,y;
    bool bol;
}b[200010*2];
void link(int x,int y)
{
    num++;
    first1[num]=y;
    next1[num]=last1[x];
    last1[x]=num;
}
int getfather(int x){return f[x]==x?x:f[x]=getfather(f[x]);}
void bfs(int s)
{
    bz[s]=true;
    int l=0,r=1;
    k[1]=s;
    while(l<r)
    {
        l++;
        int x=k[l];
        for(int i=last1[x];i;i=next1[i])
        {
            int v=first1[i];
            if(bz[v]) continue;
            bz[v]=true;
            deep[v]=deep[x]+1;
            fa[v]=x;
            k[++r]=v;
        }
    }
}
int lca(int u,int v)
{
    u=getfather(u),v=getfather(v);
    if(u==v) return u;
    if(deep[u]<deep[v]) swap(u,v);
    int t=lca(fa[u],v);
    f[u]=t;
    sz[t]+=sz[u];
}
int main()
{
    int n,m,p;
    scanf("%d %d %d",&n,&m,&p);
    for(int i=1;i<=n;i++) sz[i]=1,f[i]=i;
    for(int i=1;i<=m+p;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        b[i].x=x,b[i].y=y;
        int fx=getfather(x),fy=getfather(y);
        if(fx!=fy)
        {
            f[fy]=fx;
            b[i].bol=true;
            link(x,y); link(y,x);
        }
    }
    for(int i=1;i<=n;i++) if(!bz[i]) deep[i]=1,bfs(i);
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++) if(!b[i].bol) lca(b[i].x,b[i].y);
    for(int i=m+1;i<=m+p;i++)
    {
        int x=b[i].x,y=b[i].y;
        if(b[i].bol) printf("No\n");
        else printf("%d\n",sz[lca(x,y)]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值