• 10.31 正睿停课训练 Day13



    2018.10.31 正睿停课训练 Day13

    时间:3.5h
    期望得分:100+20+10
    实际得分:100+20+10

    又是状态很迷的一天==

    比赛链接

    A Poker(期望)

    题目链接

    容易想到枚举每一对,算它出现在多少种情况中(即(n/2*(n-2)!))。
    这样不会算重啊,虽然一个排列会枚举多次,但每次只算的是某一对的贡献,而不是当前排列的贡献。
    然后优化一下,算每个数小于它的数、小于等于它的数分别有多少个就行了。

    //29ms	1156kb
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    #define mod 1000000007
    typedef long long LL;
    const int N=1e5+5;
    
    int A[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    
    int main()
    {
    	int n=read();
    	for(int i=1; i<=n; ++i) A[i]=read();
    	std::sort(A+1,A+1+n), A[n+1]=-1;
    	LL ansA=0;
    	for(int i=1,now=1; i<=n; i=++now)
    	{
    		while(A[now+1]==A[i]) ++now;
    		ansA+=1ll*(now-i+1)*(i-1);
    	}
    	LL fac=1;
    	for(int i=2; i<=n-2; ++i) fac=fac*i%mod;
    	ansA=ansA%mod*fac%mod*(n>>1)%mod;
    	fac=fac*(n-1)%mod*n%mod;
    	printf("%lld %lld
    ",ansA,((1ll*fac*(n>>1)-ansA)%mod+mod)%mod);
    
    	return 0;
    }
    

    B Label(高斯消元)

    题目链接

    (w_x)表示(x)的点权,(len_v)表示(x o v)的边权)
    假设当前(w_x)未知,(x)周围所有点的点权(w_v)已知,那么我们要最小化$$egin{aligned}sum_{x o v}(w_v-w_x)^2cdot len_v&=sum_{x o v}(w_v^2-2w_vw_x+w_x^2)cdot len_v&=left(sum len_v ight)w_x^2+left(sum-2len_vw_v ight)w_x+sum len_vw_v^2end{aligned}$$

    (sum len_v eq0),这就是关于(w_x)的二次函数。则当$$w_x=frac{sum w_vlen_v}{sum len_v}$$

    时,(x)最优(即(w_x)(v)的加权平均数)。
    于是对每个(w_x)未知的(x),都可以列出一个方程:把已知的常数项都放到右边,对未知的(w_v),令(v)处的系数为(-len_v)即可。注意有重边。

    另外,方程组一定有解。(感觉感性理解即可,以下可以忽略。。)
    证明:
    设当前函数为(f(x_1,x_2,...,x_i,...,x_n))
    因为边权非负,且(f)存在一个有限的取值,所以(f)一定有一个最小值。
    那么一定存在一组最小的解,满足列出的方程。否则可以调整得更小:
    考虑一组最小的解(x_1,x_2,...,x_n)
    设有(m)个方程,第(i)个方程表示(x_i)周围变量的加权平均。假设有一个方程不满足加权平均的话,设(x_i')(x_i)周围变量的加权平均,那么(f(x_1,x_2,...,x_i',...,x_n)<f(x_1,x_2,...,x_i,...,x_n))(是个二次函数,所以一定存在一个点是最小值),推出矛盾。所以最小解一定满足所有方程。

    同时,方程组的解是唯一的。
    证明:
    对于一个连通块,若有一个点的权值确定,那么解是唯一的。
    具体没看懂。见图。

    //54ms	2440kb
    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    #define Sqr(x) (x)*(x)
    #define eps 1e-9
    typedef long long LL;
    const int N=505,M=60005;
    
    int Enum,H[N],nxt[M],to[M],len[M];
    double w[N];
    bool ok[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0,f=1;register char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now*f;
    }
    inline void AE(int w,int u,int v)
    {
    	if(u!=v)//自环。。
    		to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w,
    		to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
    }
    namespace G
    {
    	double A[N][N];
    	void Gauss(int n)
    	{
    		for(int i=1,j=1; i<=n&&j<=n+1; ++j)
    		{
    			if(!ok[i]) {++i; continue;}
    			int mxrow=i;
    			for(int k=i+1; k<=n; ++k)
    				if(fabs(A[k][j])>fabs(A[mxrow][j])) mxrow=k;
    			if(mxrow!=i)// std::swap(A[i],A[mxrow]);//这样效率低啊 
    				for(int k=j; k<=n+1; ++k) std::swap(A[j][k],A[mxrow][k]);
    			if(fabs(A[i][j])<eps) continue;
    			for(int k=i+1; k<=n; ++k)
    				if(fabs(A[k][j])>eps)
    				{
    					double t=A[k][j]/A[i][j];
    					for(int l=j; l<=n+1; ++l) A[k][l]-=t*A[i][l];
    				}
    			++i;
    		}
    		for(int i=n; i; --i)
    		{
    			if(!ok[i]) continue;
    			for(int j=i+1; j<=n; ++j) A[i][n+1]-=A[i][j]*w[j];
    			w[i]=A[i][n+1]/A[i][i];
    		}
    	}
    }
    
    int main()
    {
    	int n=read(),m=read();
    	for(int i=1; i<=m; ++i) AE(read(),read(),read());
    	for(int i=1; i<=n; ++i) w[i]=read();
    	for(int x=1; x<=n; ++x)
    		if(w[x]<0)
    		{
    			ok[x]=1;
    			for(int i=H[x],v; i; i=nxt[i])
    			{
    				if(w[v=to[i]]<0) G::A[x][v]-=len[i];//可能有重边 
    				else G::A[x][n+1]+=1.0*w[v]*len[i];
    				G::A[x][x]+=len[i];
    			}
    		}
    	G::Gauss(n);
    	double ans=0;
    	for(int x=1; x<=n; ++x)
    		for(int i=H[x]; i; i=nxt[i])
    			ans+=Sqr(w[to[i]]-w[x])*len[i];
    	printf("%.10lf
    ",ans*0.5);
    
    	return 0;
    }
    

    C Coin(二分图染色 博弈)

    题目链接

    10pts:用无解/有解表示是否有无法使得所有硬币朝上的情况。
    那么在一定无解的情况下,所有行列都会被选一次,所以只需判(n+m)的奇偶性就可以了。

    30pts:只需判是否无解。
    (r_i,c_i)分别表示第(i)行/第(i)列是否选择,(a_{i,j}=0(棋子朝上)/1(棋子朝下))
    那么要求对于每个有棋子的位置((i,j))(r_ioplus c_j=a_{i,j})
    (a_{i,j})可以确定(r_i,c_j)是否相同。所以行列间连边,二分图染色判是否合法即可。

    60pts:
    我们只需要判断在有解的情况下谁能得(3)分。
    根据前面的建模,我们可以得到一个图论模型。
    对于每个连通块,我们首先可以得到一种染色方案,也就是由(a)个点取(0)(b)个点取(1)。同时将每个位置取反后也是一种合法方案,即(b)个点取(1)(a)个点取(0)
    我们记这样的连通块为(a-b)连通块。
    对于一个连通块,我们走完第一步就能确定一个位置的取值必须是什么,也就是确定该方案的(a,b)分别是多少。


    这个状压感觉比较nb。。

    100pts:
    对于每个连通块,都是一个独立的平等游戏,所以可以考虑求它的(SG)值。
    考虑一个连通块的一种染色方案,假设该连通块一共有(s)个点,当前有(s_1)个点取(1),那么另一种染色方案就有(s-s_1)个点取(1)。那么当前连通块的SG值(=mathbb{mex}(sg(s_1),sg(s-s_1)))
    (sg(0)=0,sg(1)=mathbb{mex}(sg(0))=1,sg(2)=0,sg(3)=1...),也就是(sg(s)=s是奇数?0:1)
    然后把所有连通块的sg值异或起来得到(sum),后手必胜(拿到(3)分)当且仅当(sum)(0)
    (当然不用sg函数也能想到)

    //4ms	804kb
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    #define MAXIN 300000
    //#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    const int N=205,M=20005;
    
    int Enum,H[N],nxt[M],to[M],col[M],vis[N],s,s1;
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline void AE(int u,int v,int c)
    {
    	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, col[Enum]=c;
    	to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, col[Enum]=c;
    }
    bool DFS(int x,int fa)
    {
    	++s, s1+=vis[x];
    	for(int i=H[x],v; i; i=nxt[i])
    		if(vis[v=to[i]]==-1)
    		{
    			vis[v]=vis[x]^col[i];
    			if(DFS(v,x)) return 1;
    		}
    		else if(vis[v]^vis[x]!=col[i]) return 1;
    	return 0;
    }
    void Solve()
    {
    	Enum=0, memset(H,0,sizeof H), memset(vis,0xff,sizeof vis);
    	int n=read(),m=read();
    	for(int i=1; i<=n; ++i)
    	{
    		register char c=gc(); while(!isalpha(c)) c=gc();
    		switch(c) {case 'o':AE(i,1+n,0); break; case 'x':AE(i,1+n,1); break;}
    		for(int j=2; j<=m; ++j) switch(gc()) {case 'o':AE(i,j+n,0); break; case 'x':AE(i,j+n,1); break;}
    	}
    	int ans=0;
    	for(int i=1; i<=n+m; ++i)
    		if(vis[i]==-1)
    		{
    			s=s1=0, vis[i]=0;
    			if(DFS(i,i)) {printf("%d
    ",(n+m)&1); return;}
    			s-=s1;
    			if(s&1 && s1&1) ans^=1;
    			else if(!(s&1) && !(s1&1)) ans^=0;
    			else ans^=2;
    		}
    	puts(ans?"3":"2");
    }
    
    int main()
    {
    	for(int T=read(); T--; Solve());
    	return 0;
    }
    

    考试代码

    A(打表)

    容易想到答案只与数的相对大小及其数量有关。
    首先总分是(n/2*n!),如果所有数各不相同则两人都为(n/4*n!)
    否则,用暴力打表发现,一个数每多出现一次,设其总共出现次数为(x),则小M的答案减少(1*c+2*c+...+(x-1)*x)
    (c)是一个关于(n)的系数,打表后也能递推得到。

    //39ms	1940kb
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define gc() getchar()
    #define MAXIN 300000
    //#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    #define mod 1000000007
    typedef long long LL;
    const int N=1e5+5;
    
    int A[N],fac[N],ifac[N],tm[N],coef[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline int FP(int x,int k)
    {
    	int t=1;
    	for(; k; k>>=1,x=1ll*x*x%mod)
    		if(k&1) t=1ll*t*x%mod;
    	return t;
    }
    
    int main()
    {
    	int n=read(),n2=n>>1;
    	fac[0]=fac[1]=1;
    	for(int i=2; i<=n; ++i) fac[i]=1ll*fac[i-1]*i%mod;
    	for(int i=1; i<=n; ++i) A[i]=read();
    	if(n==2)
    	{
    		int ansA=(A[1]>A[2]?1:0)+(A[2]>A[1]?1:0),ansB=(A[1]>=A[2]?1:0)+(A[2]>=A[1]?1:0);
    		printf("%d %d
    ",ansA,ansB);
    		return 0;
    	}
    	std::sort(A+1,A+1+n); A[n+1]=-1;
    	LL tot=1ll*n2*fac[n]%mod,ansA=tot*FP(2,mod-2)%mod; int cnt=0;
    	for(int i=2,t=1; i<=n+1; ++i)
    		if(A[i]!=A[i-1]) tm[++cnt]=t, t=1;
    		else ++t;
    	coef[2]=1, coef[4]=4;
    	for(int i=6; i<=n; i+=2) coef[i]=1ll*coef[i-2]*((1ll*(i-2)*(i-2)+i-4)%mod)%mod;
    	LL C=1ll*FP(2,mod-2)*coef[n]%mod;
    	for(int i=1; i<=cnt; ++i)
    		ansA-=C*(tm[i]-1)%mod*tm[i]%mod;
    	ansA=(ansA%mod+mod)%mod;
    	LL ansB=(mod+tot-ansA)%mod;
    	printf("%d %d
    ",(int)ansA,(int)ansB);
    
    	return 0;
    }
    

    B

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define gc() getchar()
    #define Sqr(x) (1ll*x*x)
    typedef long long LL;
    const int N=505,M=60005;
    
    int A[N],B[N],C[N],Enum,H[N],nxt[M],to[M],len[M],dgr[N];
    
    inline int read()
    {
    	int now=0,f=1;register char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now*f;
    }
    inline void AE(int w,int u,int v)
    {
    	++dgr[v], to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
    	++dgr[u], to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
    }
    void DFS(int x,int fa,int dep)
    {
    	B[dep]=A[x];
    	for(int i=H[x]; i; i=nxt[i])
    		if(to[i]!=fa) C[dep]=len[i], DFS(to[i],x,dep+1);
    }
    inline double Check(double x)
    {
    	return 1.0*(x-B[1])*(x-B[1])*C[1]+1.0*(x-B[3])*(x-B[3])*C[2];
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    
    	int n=read(),m=read(); Enum=1;
    	for(int i=1; i<=m; ++i) AE(read(),read(),read());
    	for(int i=1; i<=n; ++i) A[i]=read();
    	if(n==2)
    	{
    		if(A[1]==-1||A[2]==-1) puts("0.0");
    		else printf("%lld.0
    ",1ll*len[2]*Sqr(A[1]-A[2]));
    		return 0;
    	}
    	int rt=1;
    	for(int i=1; i<=n; ++i) if(dgr[i]==1) {rt=i; break;}
    	DFS(rt,rt,1);
    	int cnt=0;
    	for(int i=1; i<=n; ++i) if(B[i]==-1) ++cnt;
    	if(cnt>=n-1) return puts("0.0"),0;
    	if(!cnt)
    	{
    		LL ans=0;
    		for(int i=1; i<n; ++i) ans+=Sqr(B[i+1]-B[i])*C[i];
    		printf("%lld.0
    ",ans);
    		return 0;
    	}//cnt=1
    	if(B[1]==-1) return printf("%lld.0
    ",Sqr(B[3]-B[2])*C[2]),0;
    	if(B[3]==-1) return printf("%lld.0
    ",Sqr(B[1]-B[2])*C[1]),0;
    	double l=std::min(B[1],B[3]),r=std::max(B[1],B[3]),lmid,rmid,ans=1e18;
    	for(int T=1; T<=100; ++T)//二次函数求最值啊 我还写个三分== 
    	{
    		lmid=l+(r-l)/3, rmid=r-(r-l)/3;
    		double x=Check(lmid),y=Check(rmid);
    		if(x<y) ans=std::min(ans,x), r=rmid;
    		else ans=std::min(ans,y), l=lmid;
    	}
    	printf("%.10lf
    ",ans);
    
    	return 0;
    }/*
    3 2
    1 2 3
    2 3 1
    0 -1 1
    */
    
  • 相关阅读:
    openssl生成公钥私钥对 加解密
    boost replace_if replace_all_regex_copy用法
    PS 图像滤镜— — USM 锐化
    使用boost库生成 随机数 随机字符串
    改动Android设备信息,如改动手机型号为iPhone7黄金土豪版!
    验证(Verification)与确认(Validation)的差别
    Spring-SpringMVC-Hibernate整合
    全面整理的C++面试题
    Metropolis Hasting算法
    捕捉到来自宇宙深空的神奇X-射线信号
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9886189.html
Copyright © 2020-2023  润新知