[补题]2018 BUPT Winter Training #1 div.1

26 篇文章 0 订阅
17 篇文章 0 订阅

A,B,D,F看下面:
https://blog.csdn.net/Myriad_Dreamin/article/details/79209441
C看这里:
https://blog.csdn.net/Myriad_Dreamin/article/details/79349060

CodeForces - 697D - E - Puzzle

我们知道期望拥有线性性,所以假设 i i i是待求顶点, p r e pre pre是父亲数组, s i z siz siz是以 i i i为根的子树的顶点个数, n u m num num表示 i i i的孩子的个数, s o n [ i ] son[i] son[i]表示 i i i的孩子顶点,那么:
E ( i ) = 1 + E ( p r e [ i ] ) + ∑ k = 0 n u m [ p r e [ k ] ] ∑ J ⊂ Ω n \ { i } , ∣ J ∣ = k ∑ j ∈ J s i z [ s o n [ p r e [ k ] ] [ j ] ] n u m [ p r e [ i ] ] ! = 1 + E ( p r e [ i ] ) + 1 n u m [ p r e [ i ] ] ! ( n u m [ p r e [ i ] ] ! 2 ( s i z [ p r e [ i ] ] − s i z [ i ] − 1 ) ) = E [ p r e [ i ] ] + s i z [ p r e [ i ] ] − s i z [ i ] + 1 2 \begin{array}{rl} E(i)=&\displaystyle 1+E(pre[i])+\frac{\displaystyle \sum_{k=0}^{num[pre[k]]}\sum_{J\subset \Omega_n\backslash \{i\},|J|=k}\sum_{j\in J}siz[son[pre[k]][j]]}{num[pre[i]]!} \\=&1+E(pre[i])+\displaystyle \frac{1}{num[pre[i]]!}\left(\frac{num[pre[i]]!}{2}(siz[pre[i]]-siz[i]-1)\right) \\=&E[pre[i]]+\displaystyle \frac{siz[pre[i]]-siz[i]+1}{2} \end{array} E(i)===1+E(pre[i])+num[pre[i]]!k=0num[pre[k]]JΩn\{i},J=kjJsiz[son[pre[k]][j]]1+E(pre[i])+num[pre[i]]!1(2num[pre[i]]!(siz[pre[i]]siz[i]1))E[pre[i]]+2siz[pre[i]]siz[i]+1
可能狠多人都是找规律的,这里不多加证明,而是给出一个很直观的想法:将子树捆绑在一起,它出现在每个位置的概率等同,而总的期望是长度的均值.

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N=100005,M=200005;
int pre[N],deg[N],siz[N];
const int Headsize=N,Edgesize=M;
int head[Headsize+5],mal;
struct edge{
	int nx,to;
}e[Edgesize+5];
inline void init(){
    mal=1;
    memset(head,0,sizeof(head));
}
inline void addedge(int u,int v){
	e[mal].to=v;e[mal].nx=head[u];head[u]=mal++;
}
double res[N];
void dfs(int u){
	for(int i=head[u];i;i=e[i].nx){
		int v=e[i].to;
		res[v]=res[u]+(siz[u]-siz[v]+1)/2.0;
		dfs(v);
	}
}
int main(){
	int n;
	init();
	scanf("%d",&n);
	for(int i=2;i<=n;i++){
		scanf("%d",&pre[i]);
		deg[pre[i]]++;
		addedge(pre[i],i);
	}
	res[1]=1;
	queue<int> Q;
	for(int i=1;i<=n;i++){
		if(deg[i]==0)Q.push(i);
	}
	while(Q.size()){
		int u=Q.front();Q.pop();
		siz[u]++;
		siz[pre[u]]+=siz[u];
		if(--deg[pre[u]]==0)Q.push(pre[u]);
	}
	dfs(1);
	for(int i=1;i<=n;i++){
		printf("%8lf ",res[i]);
	}
}

CodeForces - 699E - G - LRU

因为操作非常多次,所以最终的概率分布可以近似看作是稳态下的概率.那么:
对于任意一个 n n n长度的排列 P P P,若 P ( k ) P_{(k)} P(k)表示第一个元素与第 k k k个元素交换.
d p [ P ] = ∑ k = 1 n p ( P k ) d p [ P ( k ) ] dp[P]=\sum_{k=1}^n p(P_k)dp[P_{(k)}] dp[P]=k=1np(Pk)dp[P(k)]
所以:
d p [ P ] = ∑ k = 2 n p ( P k ) 1 − p ( P 1 ) d p [ P ( k ) ] dp[P]=\sum_{k=2}^n \frac{p(P_k)}{1-p(P_1)}dp[P_{(k)}] dp[P]=k=2n1p(P1)p(Pk)dp[P(k)]
这就消去了自环的影响.
再考虑替换的计算:
P = [ a 1   a 2 …   a k ] → P ′ = [ a i   a 1 …   a k − 1 ] ⇔ P ′ ′ = [ a 1 …   a k − 1 ] → P ′ = [ a i   P ′ ′ ] P=[a_1\ a_2\dots\ a_k]\to P&#x27;=[a_i\ a_1\dots\ a_{k-1}]\Leftrightarrow P&#x27;&#x27;=[ a_1\dots\ a_{k-1}]\to P&#x27;=[a_i\ P&#x27;&#x27;] P=[a1 a2 ak]P=[ai a1 ak1]P=[a1 ak1]P=[ai P]
再对 P ′ ′ P&#x27;&#x27; P中元素的全排列分类相加.可以知道这种递推是与位置无关的.设 I I I是布尔向量,那么:
d p [ I ] = ∑ i ∈ I ∑ J = I − i p ( i ) 1 − p ( J ) d p [ J ] dp[I]=\sum_{i\in I}\sum_{J=I-i}\frac{p(i)}{1-p(J)}dp[J] dp[I]=iIJ=Ii1p(J)p(i)dp[J]

#include <cstdio>
#include <algorithm>
using namespace std;
double p[25];
double dp[1<<21],res[21];
int main(){
	int n,k,s=0;
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++){
		scanf("%lf",&p[i]);
		if(p[i]<1e-6)s++;
	}
	k=min(k,n-s);
	dp[0]=1;
	double base;int sel=0;
	for(int i=0;i<(1<<n);i++){
		base=0;sel=0;
		for(int j=0;j<n;j++){
			if((i>>j)&1){
				sel++;
			}else base+=p[j];
		}
		if(sel>=k)continue;
		for(int j=0;j<n;j++){
			if(((i>>j)&1)==0){
				dp[i|(1<<j)]+=dp[i]*p[j]/base;
			}
		}
	}
	for(int i=0;i<(1<<n);i++){
		sel=0;
		for(int j=0;j<n;j++){
			if((i>>j)&1)sel++;
		}
		if(sel!=k)continue;
		for(int j=0;j<n;j++){
			if(((i>>j))&1){
				res[j]+=dp[i];
			}
		}
	}
	for(int i=0;i<n;i++){
		printf("%.8lf ",res[i]);
	}
}

CodeForces - 691E - H - Xor-sequences

这一题应该可以说是裸题了,求一个图上长度为 k k k的游动数量…
J J J是全一列向量, M M M是图度矩阵.那么总的游动数量为:
σ G ( k ) = J T M k − 1 J \sigma_G(k)=J^T M^{k-1}J σG(k)=JTMk1J

#include <cstdio>
#include <cstring>
long long a[200];
typedef long long Matrixtype;
const int Dim=101;
const Matrixtype mod=1e9+7;
struct Matrix{
    Matrixtype a[Dim][Dim];
    inline Matrixtype* const operator[](const int _i){return a[_i];}
    void setsiz(int siz){a[0][0]=siz;}
    void empty(int siz=0){
        if(siz){a[0][0]=siz;for(int i=a[0][0];i;i--)for(int j=a[0][0];j;j--)a[i][j]=0;}
        else memset(a,0,sizeof(a));
    }
    void I(int siz){a[0][0]=siz;for(int i=siz;i;i--)for(int j=siz;j;j--)a[i][j]=(i==j);}
    void J(int siz){a[0][0]=siz;for(int i=siz;i;i--)for(int j=siz;j;j--)a[i][j]=1;}
    void copy(Matrix &b){
        a[0][0]=b[0][0];
        for(int i=a[0][0];i;i--){
            for(int j=a[0][0];j;j--)a[i][j]=b[i][j];
        }
    }
    void prt(){
        puts("--------------");
        for(int i=1;i<=a[0][0];i++,puts("")){
            for(int j=1;j<=a[0][0];j++)
            	//printf("%d ",a[i][j]);
            	printf("%I64d ",a[i][j]);
        }
    }
    Matrix operator*(Matrix &b)const{
        Matrix res;
        res[0][0]=a[0][0];
        for(int i=a[0][0];i;i--){
            for(int j=a[0][0];j;j--){
                res[i][j]=0;
                for(int k=a[0][0];k;k--){
                    res[i][j]+=a[i][k]*b[k][j];
                    res[i][j]%=mod;
                }
            }
        }
        // for(int i=a[0][0];i;i--){
        //     for(int j=a[0][0];j;j--){
        //         res[i][j]%=mod;
        //     }
        // }
        return res;
    }
    void pow(Matrixtype n,Matrix &res){
        Matrix x;
        res.I(a[0][0]);
        x.copy(*this);
        while(n){
            if(n&1)res=res*x;
            x=x*x; n>>=1;
        }
    }
};
Matrix X[(sizeof(Matrixtype)<<3)+1],D,res;
void premat(Matrixtype n,Matrix &Mat){
    int k=1;X[0].copy(Mat);
    while(n>>k){
        X[k]=X[k-1]*X[k-1];k++;
    }
}
void prepow(Matrixtype n,Matrix &res){
    res.I(X[0][0][0]);
    for(int bit=0;n>>bit;bit++){
        if((n>>bit)&1)res=res*X[bit];
    }
}
bool judge(long long a,long long b){
	a^=b;
	b=0;
	while(a){
		if(a&1)b++;
		a>>=1;
	}
	return (b%3)==0;
}
int main(){
	int n;long long k;
	scanf("%d%I64d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%I64d",&a[i]);
	}
	D.I(n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(judge(a[i],a[j]))D[j][i]=D[i][j]=1;
		}
	}
	premat(k,D);
	prepow(k-1,res);
	long long ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			ans+=res[i][j];
			ans%=mod;
		}
	}
	printf("%I64d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值