zoukankan      html  css  js  c++  java
  • 【CF718E】Matvey's Birthday BFS+动态规划

    【CF718E】Matvey's Birthday

    题意:给你一个长度为n的,由前8个小写字母组成的字符串s。构建一张n个点的无向图:点i和点j之间有一条长度为1的边当且仅当:|i-j|=1或$s_i=s_j$,求这个图的直径,及直径条数。

    $nle 10^5$

    题解:首先有一个比较关键的性质:原图任意两点间的最短路不超过2*8-1。证明显然,因为每个字符在最短路上最多出现2次。

    我们先预处理一些东西:f[i][a]表示从i走到任意一个$s_j=a$的点j的最短路,g[a][b]表示从任意一个$s_i=a$的i走到$s_j=b$的j的最短路。转移时可以采用BFS,这里不详细描述。

    然后我们枚举i,试图在前面找到距离i最远的点j。点i到点j的距离不难表示成:$min{i-j,f[i][c]+f[j][c]+1}$。所以我们可以先枚举i前面的2*8-1个字符,暴力统计答案,然后考虑如何求f[i][c]+f[j][c]+1的最小值。

    有一个非常神奇的发现是:f[i][a]的取值一定是g[s[i]][a]或g[s[i]][a]+1,也就意味着我们对于每个i,都可以用一个二进制状态S表示它,其中S的第j位为1当且仅当f[i][a]=g[s[i]][a]+1。接着我们可以预处理出任意两个状态合并能得到的最小值,用v[a][b][S][T]表示,其意义为一个颜色为a,状态为S和一个颜色为b,状态为T的点合并时能得到的最小结果。然后在枚举i时我们可以枚举之前出现过的所有状态,直接调用最小值即可。

    这个大概可以叫做DP套DP了吧~

    时间复杂度:$O(nalpha+alpha^32^{2alpha}+nalpha2^{alpha})$。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    const int maxn=100010;
    typedef long long ll;
    int n,ans;
    ll sum;
    char str[maxn];
    int f[maxn][10],g[10][10],s[maxn],vis[maxn],dp[8][260],v[8][8][260][260],state[maxn];
    vector<int> p[10];
    vector<int>::iterator it;
    queue<int> q;
    int main()
    {
    	scanf("%d%s",&n,str);
    	int i,j,u,a,b,c;
    	for(i=1;i<=n;i++)	s[i]=str[i-1]-'a',p[s[i]].push_back(i);
    	memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g));
    	for(i=0;i<8;i++)
    	{
    		g[i][i]=0,vis[n+i+1]=1;
    		memset(vis,0,sizeof(vis));
    		for(it=p[i].begin();it!=p[i].end();it++)	q.push(*it),f[*it][i]=0,vis[*it]=1;
    		while(!q.empty())
    		{
    			u=q.front(),q.pop();
    			if(u!=1&&!vis[u-1])	q.push(u-1),vis[u-1]=1,f[u-1][i]=f[u][i]+1;
    			if(u!=n&&!vis[u+1])	q.push(u+1),vis[u+1]=1,f[u+1][i]=f[u][i]+1;
    			if(!vis[n+s[u]+1])
    			{
    				vis[n+s[u]+1]=1,g[s[u]][i]=f[u][i];
    				for(it=p[s[u]].begin();it!=p[s[u]].end();it++)	if(!vis[*it])
    					q.push(*it),f[*it][i]=f[u][i]+1,vis[*it]=1;
    			}
    		}
    	}
    	memset(v,0x3f,sizeof(v));
    	for(a=0;a<8;a++)	for(b=0;b<=a;b++)
    	{
    		for(i=0;i<256;i++)	for(j=0;j<256;j++)
    		{
    			for(c=0;c<8;c++)	v[a][b][i][j]=min(v[a][b][i][j],g[a][c]+g[b][c]+((i>>c)&1)+((j>>c)&1)+1);
    			v[b][a][j][i]=v[a][b][i][j];
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=max(1,i-15);j<i;j++)
    		{
    			int tmp=i-j;
    			for(c=0;c<8;c++)	tmp=min(tmp,f[i][c]+f[j][c]+1);
    			if(ans<tmp)	ans=tmp,sum=0;
    			if(ans==tmp)	sum++;
    		}
    		for(a=0;a<8;a++)	state[i]|=(f[i][a]-g[s[i]][a])<<a;
    		for(a=0;a<8;a++)	for(j=0;j<256;j++)	if(dp[a][j])
    		{
    			int tmp=v[a][s[i]][j][state[i]];
    			if(ans<tmp)	ans=tmp,sum=0;
    			if(ans==tmp)	sum+=dp[a][j];
    		}
    		if(i>15)	dp[s[i-15]][state[i-15]]++;
    	}
    	printf("%d %lld
    ",ans,sum);
    	return 0;
    }//16 aaaaaaaaaaaaaaaa 17 aaaaaaaaaaaaaaaaa
  • 相关阅读:
    SQLServer.Produce的研究
    petshop以异步方式插入订单的疑惑(后面理解了)
    SQLHelper.GetCachedParameters方法之缓存
    DALFactory抽象工厂理解
    petshop异时消息处理队列抽象工厂
    AJAX学习2(经典)
    对SQLServerDAL.order的研究(不错,有心得)
    Invertory类对商品库存的更新困惑解决了
    AJAX学习1
    用DataSet读取xml文件
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8503958.html
Copyright © 2011-2022 走看看