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; }
这题就比较有意思 问指望次数 这几种能量晶体 每次随机选出一个 也就是生成一个排列 一个一个用 问这个排列种 有多少次 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; }
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; }
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; }
!惊了好题qwq。