BZOJ 1194: [HNOI2006]潘多拉的盒子

这道题的题意也是醉了 看了半天
其实也挺水的嘛2333
搬来PO姐写的 题目大意:给定一些自动机,如果某个自动机A能产生的所有串都能在自动机B中产生,则称B是A的一个升级,求最长链

然后就不难想到两两连边接着tarjan缩点 用拓扑求个最长链就好了
怎么确定升级关系呢? N只有50啊。。
我们用一个二元组(x,y)表示走了某个串后A走到了节点x,B走到了节点y,那么如果x是输出节点而y不是,则不满足条件,否则继续广搜(x,y)的两个后继
由于这样的二元组(x,y)只有 O(n2)
总的时间复杂度就是 O(n4)

代码应该挺。。。简洁的吧

#include<bits/stdc++.h>
#define me(a,x) memset(a,x,sizeof a)
using namespace std;
const int N=52,M=N*N;
inline int read()
{
    char ch=getchar(); int x=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0'; ch=getchar();}
    return x*f;
}
struct edge{int x,y,next;}a[M],e[M]; int len,first[N];
void ins(int x,int y){a[++len]=(edge){x,y,first[x]},first[x]=len;}
int dfn[N],low[N],id,st[N],tp,bl[N],num[N],cnt,f[N],r[N];
bool in[N],bo[N][N];
void dfs(int x){
    dfn[x]=low[x]=++id,st[++tp]=x,in[x]=1;
    for(int k=first[x];k;k=e[k].next){
        int y=e[k].y;
        if(!dfn[y])dfs(y),low[x]=min(low[x],low[y]);
        else if(in[y])low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x]){
        int i; cnt++;
        do{
            i=st[tp--],in[i]=0,bl[i]=cnt,num[cnt]++;
        }while(i!=x);
    }
}
struct node{
    int n,m,p[N][2];bool op[N];
    node(){me(op,0);}
}c[N];
struct g{int x,y;};
queue<g>q;
bool bfs(int u1,int u2){
    while(!q.empty())q.pop();
    q.push((g){0,0}); me(bo,0);
    while(!q.empty()){
        g u=q.front(); q.pop();
        for(int k=0;k<2;k++){
            g o=(g){c[u1].p[u.x][k],c[u2].p[u.y][k]};
            if(bo[o.x][o.y])continue; bo[o.x][o.y]=1;
            if(c[u2].op[o.y] && !c[u1].op[o.x])return 0;
            q.push(o);
        }
    }
    return 1;
}
queue<int>Q;
int main()
{
    int n=read(),i,j;
    for(i=1;i<=n;i++){
        c[i].n=read(),c[i].m=read();
        for(j=0;j<c[i].m;j++)c[i].op[read()]=1;
        for(j=0;j<c[i].n;j++)c[i].p[j][0]=read(),c[i].p[j][1]=read();
    }
    for(i=1;i<=n;i++)for(j=1;j<=n;j++)
        if(i!=j && bfs(i,j))e[++len]=(edge){i,j,first[i]},first[i]=len;
    for(i=1;i<=n;i++)if(!dfn[i])dfs(i);
    int l=len; len=0; me(first,0);
    for(i=1;i<=l;i++)
        if(bl[e[i].x]!=bl[e[i].y])
         ins(bl[e[i].x],bl[e[i].y]),r[bl[e[i].y]]++;
    for(i=1;i<=cnt;i++)
        if(!r[i])Q.push(i),f[i]=num[i];
    int ans=0;
    while(!Q.empty()){
        int x=Q.front(); Q.pop(); ans=max(f[x],ans);
        for(int k=first[x];k;k=a[k].next){
            int y=a[k].y; r[y]--;
            f[y]=max(f[y],f[x]+num[y]);
            if(!r[y])Q.push(y);
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值