ICPCCamp2016day3 E.Intersection

链接:等camp过后挂出来再加

题意:给一组n,m表示有一个长为n的未知的字符串S,并且有m组关系,每组关系给定x,y,l表示在串S中的以第x个字符开始长度为l的子串和以第y个字符开始长度为l的子串相等。n,m<=200000。

分析:首先暴力肯定是不可取的O(m*l),那么对于这种匹配式的统计该怎么办呢?首先要知道的时候加速匹配是肯定要的,怎么加速呢?我们将每一次的关系的l分成两部分2^k长度,就像rmq的询问那样,这样是可以等价的,那么我们就有了2*m个长度都是2的次方长度。那么我们从最大指数开始处理数据,我们设f(x,y,l)表示一组关系,现在l已经处理过了,那么如果有这样3组关系f(x,y,l),f(y,z,l),f(z,x,l)那么显然这3组之间是有冗余的数据的只要保留其二就可以了,那么我们有很多种这样的数据组,我们对同长度的所有的起点求最小生成树,这里用并查集和按秩合并O(n),然后在处理完2^k层的数据后我们把剩下的<n组数据处理到2^(k-1)层去,再继续处理,最后剩下的就是都是长度为1的关系啦,然后并查集搞搞就行了,总的时间复杂度为O(nlogn)。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=250000;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const int INF=2100000000;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
typedef unsigned long long uI64;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int tot,h[25],x[30*N],y[30*N],pre[30*N];
void add(int a,int b,int c) {
    x[tot]=b;y[tot]=c;pre[tot]=h[a];h[a]=tot++;
}
int n,now,eve,fa[N],ra[N],bo[N];
int find_fa(int a) {
    if (fa[a]==a) return a;
    return fa[a]=find_fa(fa[a]);
}
int Union(int a,int b) {
    a=find_fa(a);b=find_fa(b);
    if (a==b) return 0;
    if (ra[a]>ra[b]) fa[b]=a;
    else {
        fa[a]=b;
        if (ra[a]==ra[b]) ra[a]++;
    }
    return 1;
}
void deal(int a) {
    int i,tot=0;
    eve=now;now^=1;
    for (i=1;i<=n;i++) { fa[i]=i;ra[i]=0; }
    for (i=h[a];i!=-1;i=pre[i])
    if (Union(x[i],y[i])) {
        if (a!=0) {
            add(a-1,x[i],y[i]);add(a-1,x[i]+(1<<(a-1)),y[i]+(1<<(a-1)));
        }
        tot++;
        if (tot>n) break ;
    }
}
int main()
{
    int i,k,m,a,b,c,mx;
    ll ans;
    while (scanf("%d%d", &n, &m)!=EOF) {
        mx=tot=0;memset(h,-1,sizeof(h));
        while (m--) {
            scanf("%d%d%d", &a, &b, &c);
            k=(int)log2(c);mx=max(mx,k);
            add(k,a,b);add(k,a+c-(1<<k),b+c-(1<<k));
        }
        for (i=mx;i>=0;i--) deal(i);
        for (i=1;i<=n;i++) { fa[i]=find_fa(i);bo[i]=0; }
        ans=1LL;
        for (i=1;i<=n;i++)
        if (!bo[fa[i]]) { bo[fa[i]]=1;ans=(ans*26LL)%MOD; }
        printf("%lld\n", (ans+MOD)%MOD);
    }
    return 0;
}


/*
5 5
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
8 3
1 4 3
3 4 1
4 6 3
15 2
1 6 3
2 9 7


*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值