[CSP-S模擬測試72]題解

A.簡單的序列

遇到括號匹配,先將左右括號轉化爲1和-1。ios

那麼一個括號序列合法的必要條件:總和爲0且全部時刻前綴和$\ge 0$。git

用dp預處理出長度爲$i$,總和爲$j$的括號序列數量。那麼若是p的方案數爲$dp[i][j]$,與之匹配的q的方案數即爲$dp[n-m-i][j+串m的總和]$。ide

注意須要保證統計的方案前綴和到處$\ge 0$。用原串的最小前綴和限制一下便可。spa

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+5;
typedef long long ll;
const ll mod=1e9+7;
int n,m,sum,minx;
ll dp[2005][2005];
char s[N];
int main()
{
	/*freopen("dt.in","r",stdin);
	freopen("my.out","w",stdout);*/
	scanf("%d%d%s",&n,&m,s+1);
	if(s[1]=='(')sum=minx=1;
	else sum=minx=-1;
	for(int i=2;i<=m;i++)
	{
		if(s[i]=='(')sum++;
		else sum--;
		minx=min(minx,sum);
	}
//cout<<sum<<' '<<minx<<endl;
	dp[0][0]=1;
	for(int i=1;i<=n-m;i++)
		for(int j=0;j<=i;j++)
			dp[i][j]=((j?dp[i-1][j-1]:0)+dp[i-1][j+1])%mod;//cout<<dp[i][j]<<endl;
	ll ans=0;
	for(int i=0;i<=n-m;i++)
	{
		for(int j=0;j<=i;j++)
		{
			if(j+sum>n-m||j+minx<0)continue;
			(ans+=dp[i][j]*dp[n-m-i][sum+j]%mod)%=mod;
		}
	}
	cout<<ans<<endl;
	return 0;
}

 

B.簡單的指望

設$dp[i][j][k][0/1]$爲進行i次操做,獲得數值的二進制後八位爲j,第九位開始有幾位相同,第九位是0 or 1的機率。blog

爲何取後八位呢?get

一個數含2的多少次冪其實就是它二進制表示下末尾有多少連續的0。因爲只有200次操做,取後8位能夠保證高於8位的進位最多發生一次,避免未知數量的1變成0對答案的影響。string

大力分類討論轉移便可。it

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int side=(1<<8)-1,U=230;
int n,x;
double p,dp[202][(1<<8)+5][235][2],q,ans=0.0;
int cacl(int x)
{
    int res=0;
    for( ;x%2==0;x>>=1)res++;
    return res;
}

int main()
{
    scanf("%d%d%lf",&x,&n,&p);
    p/=100.0;q=1.0-p;
    int bit=x&(1<<8);
    int cnt=1;
    if(!(x>>8))goto ak;
    for(int i=9;i<=30;i++)
    {
        int now=x&(1<<i);
        if(now!=bit)break;
        else cnt++;
    }
    ak:
    //cout<<(x&side)<<' '<<cnt<<' '<<bit<<endl;
    dp[0][x&side][cnt][bit]=1.0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<side;j++)
        {
            for(int k=1;k<=U;k++)
                dp[i][j+1][k][0]+=dp[i-1][j][k][0]*q,dp[i][j+1][k][1]+=dp[i-1][j][k][1]*q;
        }
        for(int j=1;j<=U;j++)
            dp[i][0][j][0]+=dp[i-1][side][j][1]*q;
        for(int j=1;j<=U;j++)
            dp[i][0][1][1]+=dp[i-1][side][j][0]*q;
        for(int j=0;j<128;j++)
            for(int k=1;k<=U;k++)
                dp[i][j<<1][1][0]+=dp[i-1][j][k][1]*p,dp[i][j<<1][k+1][0]+=dp[i-1][j][k][0]*p;
        for(int j=128;j<=side;j++)
            for(int k=1;k<=U;k++)
                dp[i][j<<1&side][1][1]+=dp[i-1][j][k][0]*p,dp[i][j<<1&side][k+1][1]+=dp[i-1][j][k][1]*p;
    }
    for(int i=1;i<=side;i++)
        for(int j=1;j<=U;j++)
            ans+=dp[n][i][j][0]*cacl(i)+dp[n][i][j][1]*cacl(i);
    for(int i=1;i<=U;i++)
        ans+=dp[n][0][i][0]*(8+i)+dp[n][0][i][1]*8;
    printf("%.8lf\n",ans);
    return 0;
}

 

C.簡單的操做

首先很容易發現奇環是沒法處理的,一個大奇環再怎麼合併也會剩下一個長度爲3的奇環而沒法成鏈。io

若是是一棵樹,那顯然能夠把枝杈往直徑上合併獲得最優答案。class

對於圖也同樣,偶環必定能夠縮到任意一條邊所在的鏈上。這樣一個聯通塊的答案就是它的直徑。(「最長的最短路」)

若是是不一樣聯通塊,那就把兩個端點直接連起來。答案相加便可。

//不要卡spfa啊!!!
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#define re register
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=1e3+5,M=2e5+5;
int n,m;
int to[M],head[N],nxt[M],tot;
int col[N],bel[N],part,dis[N],v[N],len[N],ans;
inline void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int x,int c,int now)
{
	bel[x]=now;col[x]=c;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(!col[y])dfs(y,-c,now);
		else if(col[x]==col[y])
		{
			puts("-1");exit(0);
		}
		//cout<<x<<' '<<y<<endl;
	}
}
inline int spfa(int s)
{
	queue<int> q;
	for(re int i=1;i<=n;i++)
		dis[i]=0x3f3f3f3f,v[i]=0;
	dis[s]=0;q.push(s);v[s]=1;
	int maxx=0;
	while(!q.empty())
	{
		int x=q.front();q.pop();
		maxx=max(maxx,dis[x]);
		for(re int i=head[x];i;i=nxt[i])
		{
			int y=to[i];
			if(dis[y]>dis[x]+1)
			{
				dis[y]=dis[x]+1;
				if(!v[y])v[y]=1,q.push(y);
			}
		}
		v[x]=0;
	}
	return maxx;
}
int main()
{
	/*freopen("dt.in","r",stdin);
	freopen("my.out","w",stdout);*/
	n=read();m=read();
	for(re int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		add(x,y);add(y,x);
	}
	for(re int i=1;i<=n;i++)
		if(!col[i])dfs(i,1,++part);

	for(re int i=1;i<=n;i++)
		len[bel[i]]=max(len[bel[i]],spfa(i));
	for(re int i=1;i<=part;i++)
		ans+=len[i];
	cout<<ans<<endl;
	return 0;
		
}

 

 

 

便可真是個美妙的詞語

相關文章
相關標籤/搜索