JZOJ5711. 【北大夏令营2018模拟5.13】时间幻象

这里写图片描述
这里写图片描述

题目大意

要求出一个长度为n,且仅由ABCD四个字母组成的字符串,
通过给出的m种变化方式,以及任意交换相邻的两个,得出尽可能多的字符串。

题解

任意交换相邻的两个位置,也就是说当ABCD的个数确定的时候,就可以通关交换两个相邻的,弄出全排列。
其实,只需要记录ABC的个数就可以了,D的个数是可以直接算出来的。
n只有30,估算一下最多的状态数只有不到6000,那么考虑转移,也就是只有m*状态数。
这样以ABC的数量为点,根据给出的条件连有向边。
现在题目就变成了求图中的最长路,
就先用tarjan缩点,对于一个强连通分量里面是所有点,可以从任意一个位置进去,然后走完整个环之后,再从任意一个位置出来,根据拓扑序求出最长路就好了。

code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#define ll long long
#define N 150003 
#define M 33
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int ti,dfn[N],low[N],z[N],g[N],cnt,ta;
int lst[N],nxt[N],to[N],tot;
int lst_[N],nxt_[N],to_[N],tot_;
int n,m,t1[5],t2[5],t[5],id[M][M][M],w,tt;
int q[N],d[N],l,r,x;
int s[M][M],sum[M];
long long f[N],V[N],F[N],ans;
bool bz[N];

void tarjan(int x)
{
    ti++;
    dfn[x]=low[x]=ti;
    bz[x]=0;
    ta++;
    z[ta]=x;
    for(int i=lst[x];i;i=nxt[i])
    {
        if(dfn[to[i]]==0)
        {
            tarjan(to[i]);
            low[x]=min(low[x],low[to[i]]);
        }
        else if(!bz[to[i]])low[x]=min(low[x],dfn[to[i]]);
    }
    if(low[x]==dfn[x])
    {
        for(cnt++;z[ta]!=x && ta;ta--)F[g[z[ta]]=cnt]+=V[z[ta]],bz[z[ta]]=1;
        F[g[z[ta]]=cnt]+=V[z[ta]],bz[z[ta]]=1;
        ta--;
    }
}

void ins(int x,int y)
{
    nxt[++tot]=lst[x];
    to[tot]=y;
    lst[x]=tot;
}

void ins_(int x,int y)
{
    nxt_[++tot_]=lst_[x];
    to_[tot_]=y;
    lst_[x]=tot_;
}

void re()
{
    memset(t1,0,sizeof(t1));
    memset(t2,0,sizeof(t2));
    for(ch=G();ch<'A' || ch>'D';ch=G());
    for(;'A'<=ch && ch<='D';ch=G())t1[ch-'A']++;
    for(ch=G();ch<'A' || ch>'D';ch=G());
    for(;'A'<=ch && ch<='D';ch=G())t2[ch-'A']++;
    for(int i=0;i<5;i++)t[i]=t2[i]-t1[i];
}

void pre()
{
    for(register int i=1;i<M;i++)
    {
        int t=i;
        for(register int j=2;j<M;j++)
        {
            s[i][j]+=s[i-1][j];
            for(;t%j==0;t=t/j)s[i][j]++;
        }
        s[i][t]++;
    }
}

ll ksm(ll x,int y)
{
    ll s=1;
    for(;y;y>>=1,x=x*x)
        if(y&1)s=s*x;
    return s;
}

void get()
{
    for(register int i=0;i<=n;i++)
        for(register int j=0;j+i<=n;j++)
            for(register int k=0;k+i+j<=n;k++)
            {
                V[id[i][j][k]]=1;
                for(int w=2;w<M;w++)
                    V[id[i][j][k]]*=ksm(w,s[n][w]-s[i][w]-s[j][w]-s[k][w]-s[n-i-j-k][w]);
            }
}

int main()
{
    freopen("return.in","r",stdin);
    freopen("return.out","w",stdout);

    pre();read(n);
    for(register int i=0;i<=n;i++)
        for(register int j=0;j+i<=n;j++)
            for(register int k=0;k+i+j<=n;k++)
                id[i][j][k]=++w;
    for(read(m),get();m;m--)
    {
        re();
        for(register int i=0;i<=n;i++)
            if(t[0]+i<=n && i>=t1[0])
            for(register int j=0;j+i<=n;j++)
                if(t[1]+j<=n && j>=t1[1])
                for(register int k=0;k+i+j<=n;k++)
                    if(t[2]+k<=n && k>=t1[2])
                    {
                        tt=n-i-j-k;
                        if(t[3]+tt>n || tt<t1[3])continue;
                        ins(id[i][j][k],id[i+t[0]][j+t[1]][k+t[2]]);
                    }       
    }

    memset(bz,1,sizeof(bz));
    for(register int i=1;i<=w;i++)
        if(dfn[i]==0)tarjan(i);

    for(register int i=1;i<=w;i++)
        for(register int j=lst[i];j;j=nxt[j])
            if(g[i]!=g[to[j]])ins_(g[i],g[to[j]]),d[g[to[j]]]++;

    for(register int i=1;i<=w;i++)
        if(d[i]==0)q[++r]=i,f[i]=0;

    for(l=0;l<r;)
    {
        f[x=q[++l]]+=F[x];
        for(register int i=lst_[x];i;i=nxt_[i])
        {
            f[to_[i]]=max(f[to_[i]],f[x]);
            d[to_[i]]--;
            if(d[to_[i]]==0)q[++r]=to_[i];
        }
        ans=max(ans,f[x]);
    }
    printf("%lld",ans);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值