1. 程式人生 > >10.31 正睿停課訓練 Day13

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\n",ansA,((1ll*fac*(n>>1)-ansA)%mod+mod)%mod);

    return 0;
}

B Label(高斯消元)

題目連結

\(w_x\)表示\(x\)的點權,\(len_v\)表示\(x\to v\)的邊權)
假設當前\(w_x\)未知,\(x\)周圍所有點的點權\(w_v\)已知,那麼我們要最小化\[\begin{aligned}\sum_{x\to v}(w_v-w_x)^2\cdot len_v&=\sum_{x\to v}(w_v^2-2w_vw_x+w_x^2)\cdot len_v\\&=\left(\sum len_v\right)w_x^2-\left(\sum-2len_vw_v\right)w_x+\sum len_vw_v^2\end{aligned}\]

\(\sum len_v\neq0\),這就是關於\(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\n",ans*0.5);

    return 0;
}

C Coin

題目連結

考試程式碼

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\n",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\n",(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\n",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\n",ans);
        return 0;
    }//cnt=1
    if(B[1]==-1) return printf("%lld.0\n",Sqr(B[3]-B[2])*C[2]),0;
    if(B[3]==-1) return printf("%lld.0\n",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\n",ans);

    return 0;
}/*
3 2
1 2 3
2 3 1
0 -1 1
*/