草 由于今天的排名很 (fake),所以就不粘了
T1: 30 ( ightarrow) 0
T3: 20 ( ightarrow) 100
好多计数题啊 打快读打的最少的一场
T1:阴阳
神仙恶心人的 DP
T2:简单的序列
令左括号为 (1),右括号为 (-1)。刚学栈的时候我们就知道要求括号序列的前缀和恒大于等于 (0),且最后为 (0)。
预处理 (f[i][j]) 表示 (i) 个括号和为 (j) 的方案数。
设最后 (p) 序列的方案数为 (f[i][j]),给出的序列的值为 (res),那么 (q) 序列的方案就应该是 (f[n-m-i][-j-res]),但下标不能为负,但我们发现 (f[n-m-i][-j-res]) 和 (f[n-m-i][j+res]) 是相等的(左右括号互换而已)。直接乘积加上。
注意要求括号序列时时合法,有一些恶心的边界要判。其中重要的就是要记录计算原有序列的最小前缀值,从而确定 (p) 序列左括号的下界。
T3:简单的期望
如题,简单的期望。只需 printf("%lf
",0.0/0.0)
即可。
同样是神仙 DP。可以发现,最多进行 200 次加法,所以大于八位的进位只会进行一次。所以设 (f[i][j][k][0/1]) 表示操作前 (i) 次,最后 8 位状态是 (j),从第 9 位开始有连续 (k) 位相同,第 9 位为 (0/1) 的概率。
直接看代码吧。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn=300+10;
const int base=(1<<8)-1;
int x,n,p;
int bit[maxn];
long double pmul,padd,ans;
long double f[maxn][maxn][maxn][2];
//操作前i次 最后8位状态是j 从第9位开始有连续k位相同 第9位为0/1的概率
int main(){
#ifndef LOCAL
freopen("exp.in","r",stdin);
freopen("exp.out","w",stdout);
#endif
scanf("%d%d%d",&x,&n,&p);
pmul=1.0*p/100;padd=1.0*(100-p)/100;
int temp=x>>8,flag=temp&1,cnt=0;
if(temp==0)cnt=1;
else while((temp&1)==flag){
cnt++;temp>>=1;
}
f[0][x&base][cnt][flag]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=base;j++)
for(int k=1;k<=base;k++){
f[i][j+1][k][0]+=f[i-1][j][k][0]*padd;
f[i][j+1][k][1]+=f[i-1][j][k][1]*padd;//直接加 不进位
}
for(int k=1;k<=base;k++){
f[i][0][k][0]+=f[i-1][base][k][1]*padd;//加 超级进位
f[i][0][1][1]+=f[i-1][base][k][0]*padd;//加 进一位
}
for(int j=0;j<=base/2;j++)//j最高位是0
for(int k=1;k<=base;k++){
f[i][j<<1][1][0]+=f[i-1][j][k][1]*pmul;//乘 第9位为1
f[i][j<<1][k+1][0]+=f[i-1][j][k][0]*pmul;//乘 第9位为0
}
for(int j=base/2+1;j<=base;j++)//j最高位是1
for(int k=1;k<=base;k++){
f[i][j<<1&base][1][1]+=f[i-1][j][k][0]*pmul;//乘 第9位为0
f[i][j<<1&base][k+1][1]+=f[i-1][j][k][1]*pmul;//乘 第9位为1
}
}
for(int i=1;i<=base;i++){
int now=i;
while((now&1)==0){
now>>=1;bit[i]++;
}
}
for(int j=1;j<=base;j++)
for(int k=1;k<=base;k++){
ans+=1.0*(f[n][j][k][0]+f[n][j][k][1])*bit[j];
}
for(int k=1;k<=base;k++)
ans+=1.0*f[n][0][k][0]*(k+8)+1.0*f[n][0][k][1]*8;
printf("%.10Lf
",ans);
return 0;
}
T4:简单的操作
首先,考场上看错题了,没看到合并的两点不能直接相连。
那么,首先存在奇环的情况一定无解,因为他最终会缩成一个三元环,然后就 GG 了。判奇环的方法是黑白染色,详情可以看老姚博客
如果是一棵树,最优的情况一定是合并成一条直径。那么对于一个普通的联通块来说呢?直径就是两点之间最短路的最大值。所以答案就是所有联通块的最短路的最大值的和。
由于是无权图,所以用 bfs 即可 (O(nm)) 求解。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
const int maxm=1e5+10;
int n,m;
long long ans;
int len[maxn];
struct Edge{
int from,to,nxt;
}e[maxm<<1];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int head[maxn],cnt;
inline void add(int u,int v){
e[++cnt].from=u;
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int tot;
int col[maxn],belong[maxn];
void dfs(int u,int now){
belong[u]=now;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!col[v]){
col[v]=-col[u];dfs(v,now);
}else if(col[u]==col[v]){
puts("-1");exit(0);
}
}
}
int Time;
int dis[maxn],vis[maxn];
int bfs(int s){
int res=0;
queue<int> q;
q.push(s);dis[s]=0;vis[s]=Time;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v]==Time)continue;
vis[v]=Time;q.push(v);
dis[v]=dis[u]+1;
res=max(res,dis[v]);
}
}
return res;
}
int main(){
#ifndef LOCAL
freopen("merge.in","r",stdin);
freopen("merge.out","w",stdout);
#endif
n=read();m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++)
if(!col[i]){
col[i]=1;dfs(i,++tot);
}
for(int i=1;i<=n;i++){
Time++;
len[belong[i]]=max(len[belong[i]],bfs(i));
}
for(int i=1;i<=tot;i++)
ans+=len[i];
printf("%lld
",ans);
return 0;
}