您的位置:首页 > 其它

题目<2016/11/30>

2016-11-30 21:17 141 查看
1.HDU5731

轮廓线状压DP+裸的容斥原理。

首先考虑没有任何限制的多米诺骨牌的问题。

那么我们暴力DP的话就是让不放的为0,放了的为1.

但是太暴力了,我们可以这样想。

对于第i个位置,我们实际上只关心第i个位置的:



如果我们知道这个轮廓线的所有状态,我们通过枚举这个格子怎么放也可以达到效果。

步骤如下:

1.枚举轮廓线状态以及在轮廓线的最后一个格子的状态。

2.更新下一个格子的轮廓线的状态,容易注意到,我们的轮廓线只是平移了一个单位。

3.所以我们用位运算表示轮廓线的位置,非常好做。

int f[2][1 << 17];
void Add(int s,int y)
{
int &x = f[cur][s & ((1 << m) - 1)];
if(s & (1 << m))x += y;
if(x >= Mod)x -= Mod;
}
int solve()
{
cur = 0;
if(n < m)swap(n,m);
int S = (1 << m) - 1;
f[0][S] = 1;
for(int i = 0;i < n;++ i)
{
for(int j = 0;j < m;++ j)
{
cur ^= 1;
memset(f[cur],0,sizeof(f[cur]));
for(int s = 0;s <= S;++ s)
{
Add((s << 1),f[cur ^ 1][s]);
if(j && !(s & 1))Add( ((s << 1) | 3),f[cur ^ 1][s]);
if(i && !(s & (1 << (m - 1))))Add((s << 1) | 1 | (1 << m),f[cur ^ 1][s]);
}
}
}
return f[cur][S];
}


注意向上的答案,需要用到m+1个元素。

就没别的好说的了。

预处理这个多米诺之后,我们通过容斥原理是很容易算出答案的。

有两个形式的容斥原理:

1.枚举至少满足每个状态,如果这个状态有偶数个1那么就减,否则就加。

2.枚举第一个违反条件的地方i,这个答案可以通过任意选减去第一次违反条件在j(j

void cal()
{
while(~scanf("%d%d",&n,&m))
{
m --;
int S = 1 << m,ans = 0;
for(int s = 0;s < S;++ s) //枚举列的分割
{
int cur = 0,cnt = 0;
for(int i = 0;i < m;++ i)
{
if(s & (1 << i))a[++ cnt] = 1 + cur,cur = 0;
else ++ cur;
}
a[++ cnt] = cur + 1;
for(int i = 1;i <= n;++ i)//枚举强制在第i行
{
for(int j = 0;j < i;++ j)
{
int re = 1;
for(int k = 1;k <= cnt;++ k)
re = 1ll * re * f[a[k]][i - j] % Mod;
if(!j)dp[i] = re;
else dp[i] = (dp[i] - 1ll * re * dp[j] % Mod + Mod) % Mod;
}
}
if(cnt & 1)ans = (ans + dp
) % Mod;
else ans = (ans + Mod - dp
) % Mod;
}
printf("%d\n",ans);
}
}


SRM664.DIV1T2

题意是:

给你棵树,以1为根。现在要炸这个树。

点i没炸掉的几率是1/i。

现在问所有联通块的大小的平方的期望值。

感觉很神,首先我们用奇怪的方法把平方去掉。

转成树上有序点对数目。

用dp[x]表示点x能到达子树(含自己)的期望点的个数。

那么dp[x]=∑v∈son[x]dp[v]∗pr[x]

这个方程的意义在于,v能到达的点在x没炸的时候一定能到达。

怎么算答案呢?

我们用类似于点分治的想法(并不),考虑过子树根的点对(路径)对答案的贡献,以及根本身对答案的贡献,可知一共分三种情况。

1.根本身:pr[x]

2.根到子树里面:pr[x]∗dp[v]

3.过根的路径:dp[v]∗(dp[x]−dp[v]∗pr[x])
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: