指望題選作

P 4316 一張無向圖之上求從1到n通過路徑總長度的指望。看起來 很不可作 可是因爲 線性運算的指望==指望的線性運算 這道題變得可作起來了。ios

別問我這個等式 我很迷的利用其解決一些問題,我還沒入門 服了。咱們倒推 我也不知道爲何要倒推可是顯然的是 終點是一切指望的開始。c++

面對這些題目 我是該沉思我何時能秒題呢?何時才能 所有都理解呢,我指望 那一天 很快到來吧。算法

論 指望: 我見到的題目大體按照個人感受能夠分爲兩種 一種是條件指望 一種是排列指望。數組

對於條件指望,其實就是 在知足 任意可能性的狀況之下 達到的狀態 這個狀態也以另外一種可能性達到另外一個狀態。ide

顯然有咱們把全部可能的結果都生成 用其結果*其機率就是咱們的均值指望了。不過這樣顯然是不可能的當事件過多咱們不可能把全部的狀況列出來從而獲得結果。優化

可是指望有一個 最偉大的性質是 線性可加的。有限個隨機變量之和的數學指望==每一個隨機變量的數學指望之和。spa

爲何 ?3d

看起來如此的顯然 在我眼裏至關的 難以想象誒。下一刻 怎麼這麼顯然我都看不出來。code

仔細思考 發現的確很顯然吧 在某種角度來看。接下來作題 從個人那個顯然的角度出發。blog

有向無環圖 考慮f[x] 表示從x 到 n的指望跑過的路徑長度。那麼 遞推式子就很好寫了。

註明一個 容易wa 的點 : 關於精度的問題當讓保留幾位小數的時候printf 輸出限定幾位小數是會四捨五入的。這裏容易出錯一旦四捨五入的話,形成精度問題。

這類問題在實數二分的時候會常常出現 通常的比較準確的是獲得答案ans 後 將ans *10^x 賦給一個整形的變量 而後 再賦給一個double型的 /10^x 而後就行了 沒有什麼四捨五入 這點要注意。

或者就是直接 整數二分 所有擴大 最後再除 保證不會出現四捨五入的狀況出現。這裏注意 有些題就是讓四捨五入。

中午頹博客+睡覺好像是慣例了吧。。畢竟 人心是須要溫暖的。

頭腦清醒的時候效率果真高!

P1297 這就是上述式子的應用 咱們發現 枚舉出全部的狀況是不可能的 既然線性可加 那麼咱們本身求本身的指望 累加在一塊兒就是整體的。

對於某一題 它做對的指望之和它上一題有關 總狀況顯然是 a[i]*a[i-1] 做對的可能狀況有min(a[i],a[i-1])種那麼咱們求每一道題的指望 並將其累加就獲得了答案。

//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 1000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
#define up(p,i,w) for(ll i=p;i<=w;++i)
using namespace std;
const ll MAXN=10000000;
ll n,A,B,C;
ll a[MAXN];
db ans;
int main()
{
    freopen("1.in","r",stdin);
    scanf("%d%d%d%d%d",&n,&A,&B,&C,a+1);
    for (ll i=2;i<=n;i++)
    a[i]=((ll)a[i-1]*A+B)%100000001;
    for(ll i=1;i<=n;i++)
    a[i]=a[i]%C+1;
    //for(ll i=1;i<=n;++i)cout<<a[i]<<endl;
    ans+=(db)(min(a[n],a[1]))/((db)a[1]*a[n]);
    for(ll i=2;i<=n;++i)ans+=(db)(min(a[i],a[i-1]))/((db)a[i]*a[i-1]);
    printf("%.3lf",ans);
    return 0;
}
View Code

這題就比較有意思 問指望次數 這幾種能量晶體 每次隨機選出一個 也就是生成一個排列 一個一個用 問這個排列種 有多少次 1-7這連續在一塊兒。且這個排列也是隨機生成的。

首先考慮有多少種排列 n!->n!/a1!a2!a3!a4!a5!a6!a7!這玩意好像是的...很容易看出來。

考慮在這麼多種排列之中有多少狀況是合法的。顯然一個排列想要合法狀況的數量上界爲 設ans=min(a1,a2,a3,a4,a5,a6,a7);

也就是說其中的必然有一個排列的自身的價值爲ans 想辦法統計答案就行了。彷佛不太能寫,咱們固訂價值 找 排列數量 很是難找。除了極端狀況。

那麼此時考慮 生成一個合法序列的機率有多大?此時也是指望 覺得生成的話價值爲1 乘起來就是了。

不考慮排列此時 那麼咱們選取第一個的機率爲 a1/n 第二個的機率爲 a2/n-1......這樣 考慮排列呢 其實也沒有什麼關係 由於 這樣每種排列的成功機率都同樣。

乘上排列數量就行了吧 那麼這是價值爲1的指望 價值爲2 呢 咱們此時由於是線性可加的而那些失敗的不帶來價值不進行累加。

顯然有狀態 f[i] 表示 前i個魔法造成的指望價值是多少:顯然的是1-7 是7!*a1*...a7/n*(n-1)*...(n-6);

後面 每一項 的機率都是這個玩意 , 很崩潰對麼 我 也很...沒有日後面的指望和前面的同樣這個方面來思考。

證實:譬如第8項咱們隨便給他找一個數字 由於咱們前7項是一個排列 a1吧 此時機率就是前面那堆東西 a1-1/n-7此時纔會有價值的出現。

而後 咱們發現對於2-8這個東西排列也是7!且 前面第一個數字可能性有7種 咱們把這七種可能加起來發現後面這個sigama ax-1/n-7==1

而後 乘上上述價值恰好是第一個排列的指望。因而答案就是 7!*第一個排列的指望*n-6。

//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 1000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=10;
db ans=1;
ll a[MAXN],n,flag=10;
int main()
{
    //freopen("1.in","r",stdin);
    for(ll i=1;i<=7;++i)a[i]=read(),n+=a[i],flag=min(flag,a[i]);
    if(!flag){puts("0.000");return 0;}
    for(int i=1;i<=7;++i)ans*=(db)((db)a[i]/(n-i+1));
    for(ll i=1;i<=7;++i)ans=ans*i;
    ans=ans*(n-6);
    printf("%.3lf",ans);
    return 0;
}
View Code

LINK:小豬佩奇玩遊戲 指望基礎題目 可是我好像不會寫來着當時一眼也沒看出來正解因此棄療了。

思路仍是比較容易獲得的 考慮每一個數字都要消除 一次的話難免有其餘的數字會給其帶來影響 因此一個數字消除的機率是 1/(s+1) 其中s 是一些數字通過k次冪後獲得的個數。

發現n只有1e9 一些數字是沒有平方根或者立方根的 因此考慮枚舉平方根立方根...生產對應的數字 那麼這樣的範圍也就是sqrt(n)了。

生成的數字最多 每次log個 hash存起來最後便利一下鄰接表便可。思路仍是比較清晰的(map好像會T的樣子。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 100000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define EPS 1e-5
#define P 19260817ll
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int mod=1000003;
int T;
int n,m,len;
db ans=0;
int lin[mod],nex[mod],e[mod];
int ver[mod];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline int calc(int x,int y,int z)
{
    for(int i=lin[x];i;i=nex[i])
    {
        if(ver[i]==y)
        {
            e[i]+=z;
            return e[i];
        }
    }
    return 0;
}
inline int find(int x)
{
    int s=x*P%mod;
    for(int i=lin[s];i;i=nex[i])
    {
        if(ver[i]==x)return e[i];
    }
    return 0;
}
inline void insert(int w,int y)
{
    int s=w*P%mod;
    if(!calc(s,w,y))add(s,w,y);
}
int main()
{
    freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        memset(lin,0,sizeof(lin));
        len=0;n=read();ans=0;
        m=(int)(sqrt(1.0*n));
        //cout<<m<<endl;
        for(int i=2;i<=m;++i)
        {
            ll w=i*i;
            while(w<=n)
            {
                insert(w,1);
                w=w*i;
            }
        }
        for(int i=1;i<=len;++i)
        {
            int w=find(ver[i]);
            ans=ans+1.0/((w+1)*1.0);
        }
        ans=ans+(n-len);
        printf("%.5lf\n",ans);
    }
    return 0;
}
View Code

 LINK:時間跳躍 這道指望的題目我作的一塌糊塗 什麼都不是。寫在前面 我應該盡全力去思考。

題目大意就是 隨便選一些邊構成的凸多邊形的指望值。一個比較顯然的想法是爆搜 能夠獲得20分。

至於斷定條件 如何構成凸多邊形 顯然的是 類比於三角形 最長邊不能大於其餘邊之和 。

咱們容易想到設狀態 f[i] 表示 最長邊和其餘邊差值爲j的指望 這個東西難以轉移下去不知道方案數大體是這樣的。

考慮實質上仍是 若干邊和其餘邊的組合 咱們考慮枚舉一個最長邊 那麼其能帶來的貢獻 其餘小於它的邊構成的比其長的集合 帶來的貢獻了。

當前邊是 i 那麼就是前i-1條邊所構成的邊的長度要大於i 這裏有一個 不須要考慮的細節圍成多邊形顯然的是 至少3條邊可是除了最長的邊 剩下的邊再想大於當前邊至少須要2條。

因而有了這樣的一個算法 即 f[i][j] 表示前i條邊構成長度爲j的方案數,好像少點什麼 代價 加上去吧 f[i][j][k] 其中k表示此時用了多少條邊。

轉移仍是很顯然的時間n^3 且對空間消耗較大。(其實能夠考慮打表優化了但好像也不行的樣子由於j須要枚舉的範圍實在是太大了。

考慮不合法的 只有[1,i]這個範圍咱們求出這個範圍中的東西 全集減一下便可。全集是什麼 對於某個i來講考慮選了j條邊乘以代價即全集最後別忘了要成機率。

空間複雜度 通過滾動數組就能夠優化到n^2了 打表 便可。

考慮如何把時間優化到n^2 考慮再設一個數組g[i][j]表示 前i個數構成j的邊數的代價 f[i][j] 表示前i個數構成j的方案數。

f數組的轉移顯然是01揹包。g數組?g[i][j]=g[i-1][j]+g[i-1][j-i]+f[i-1][j-i];這樣咱們就成功把時間複雜度降到了n^2.

 //#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define mod 1000000007
#define ull unsigned long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ll MAXN=5010;
ll maxx,T;
ll f[2][MAXN],fac[MAXN];//f[i][j]表示前i個數構成j的方案數
ll g[2][MAXN];//g[i][j]表示前i個數構成j的權值和
ll w[MAXN],ans[MAXN],b[MAXN],c[MAXN],inv[MAXN];
ll v[MAXN],s[MAXN];
//w[i]表示前i條邊的答案 c[i] 表示第i條邊的全集和
inline ll fast_pow(ll b,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)ans=ans*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return ans;
}
inline void prepare()
{
    fac[0]=1;
    for(ll i=1;i<=maxx;++i)fac[i]=fac[i-1]*i%mod;
    inv[maxx]=fast_pow(fac[maxx],mod-2);
    for(ll i=maxx-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(ll a,ll b){return fac[a]*inv[b]%mod*inv[a-b]%mod;}
signed main()
{
    //freopen("1.in","r",stdin);
    T=read();
    for(ll i=1;i<=T;++i)
    {
        ans[i]=read();
        maxx=max(maxx,ans[i]);
    }
    ll u=0;
    f[u][0]=1;
    prepare();
    for(ll i=1;i<=maxx;++i)
    {    
        for(ll j=0;j<=i;++j)
        {
            w[i]=(w[i]+g[u][j])%mod;
            s[i]=(s[i]+f[u][j])%mod;
        }
        u=u^1;
        for(ll j=maxx;j>=0;--j)
        {
            f[u][j]=f[u^1][j];
            if(j>=i)f[u][j]=(f[u][j]+f[u^1][j-i])%mod;
            g[u][j]=g[u^1][j];
            if(j>=i)g[u][j]=(g[u][j]+g[u^1][j-i]+f[u^1][j-i])%mod;
        }
    }
    //考慮求出答案 全集減補集
    b[0]=1;
    for(ll i=1;i<=maxx;++i)
    {
        //對於第i條邊做爲最長邊 補集的代價爲w[i]+s[i]都是不和法的。
        //對於強制選擇第i條邊的全集爲 C(i-1,j)*j+C(i-1,j);
        v[i]=v[i-1];
        b[i]=b[i-1]*inv[2]%mod;
        ll sum=0;
        for(ll j=0;j<=i-1;++j)
            sum=(sum+C(i-1,j)*(j+1))%mod;
        //cout<<i<<' '<<sum<<' '<<sum-w[i]-s[i]<<endl;
        v[i]=((v[i]+sum-w[i]-s[i])%mod+mod)%mod;
    }
    for(ll i=1;i<=T;++i)
    {
        ll x=ans[i];//第x條邊的答案
        printf("%lld\n",v[ans[x]]*b[ans[x]]%mod);
    }
    return 0;
}
View Code

!驚了好題qwq。

相關文章
相關標籤/搜索