题解
又是lzw大佬出题…不过这次的题好像他也自己以前做过。感觉上并不是特别难但….只是感觉上orz
第一题——游戏(game)
【题目描述】
- 两个人玩游戏,A每次做任务有 的概率得到 分,B每次只会做难度为1的任务,得分为n时即是获胜。求A获胜的概率。
- 这个概率题真的是….orz我盯着样例看了半天没看懂什么意思…最后只交了个样例,还好数据有点良心,骗了十分。
- 正解是利用dp方程地推+记忆化搜索。
- 由于要得到n分,那干脆直接反过来想。两个人初始有n分,A先到0分的概率是多少。那就可以进行枚举。
- 假设A有p的概率获得k分,则有1-p的概率不得分。那方程就可以写成:
- 由于B的得分也是有概率的那么就需要进行对B的分类了。重新写下方程就是
- 调整下顺序:
- 左右同乘2
然后接着去枚举p和k就好了…(记得是p=k<<1)
- 但是好像直接枚举p有点不现实啊…那就把它转化下变成:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
void fff(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
}
int n;
double f[1001][1001];
double dp(int x,int y){
if(x<=0) return 1.0;
if(y<=0) return 0.0;
if(f[x][y]>=0){
return f[x][y];
}
for (int k=1,p=2;k<=1024;p<<=1,k<<=1){
double temp;
temp=dp(x-k,y-1)+dp(x-k,y)+(double)(p-1)*dp(x,y-1);
temp/=(double)(p+1);
f[x][y]=max(f[x][y],temp);
}
return f[x][y];
}
int main(){
fff();
scanf("%d",&n);
for (int i=0;i<=1000;i++){
for (int j=0;j<=1000;j++)
f[i][j]=-1000;
}
printf("%.6lf",dp(n,n));
}
第二题——牛的杂技(acrobat)
【题目描述】
- n头牛叠罗汉,各自有重量和力量 ,定义每头牛的压扁指数是在他之上的所有牛的质量之和-他的力量。压扁指数就是被压得最扁的那头奶牛的压扁指数。现求尽量小的压扁指数。
- 这个一看就知道是排序ex之类的问题了orz。小小证明下:
- 对于相邻两头牛有 ,在他们之上的重量和为 。
- 那么交换前和交换之后的压扁指数为:
- 现在假设交换前比交换之后的解更优则:
- 左右同时减去 ,并加上 则:
然后就是常规的排序题了。
但是这个无良数据居然还有负数orz首测只得了60
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
void fff(){
freopen("acrobat.in","r",stdin);
freopen("acrobat.out","w",stdout);
}
const int MAXN=51100;
int n;
struct node{
LL w,s;
LL sum;
bool operator < (const node x) const{
if(sum==x.sum) return s<x.s;
return sum<x.sum;
}
}a[MAXN];
int main(){
// fff();
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].w,&a[i].s);
a[i].sum=a[i].w+a[i].s;
}
sort(a+1,a+n+1);
LL s=0,ans=-999999999;
for (int i=1;i<=n;i++){
ans=max(ans,s-a[i].s);
s+=a[i].w*1ll;
}
printf("%lld",ans);
}
第三题——过路费(cost)
【题目描述】
- 给出n个点和m条双向边。定义两点之间的花费价值为路径上的最长路径长度。现给出q个查询,求出x到y的花费价值。
- 真的,看到这里你会觉得第一题真**难。看这个题就知道是最小生成树+LCA的板子题。
- 看我五分钟给你打完LCA!不就是个最小生成树么!
但是!我LCA少了个弹出orz。今日爆零…
没什么好讲的吧其实..两点之间存在短的路径就走短的路径,克鲁斯卡尔算法解决。
- 两点间的查询由倍增LCA来解决。无非是把倍增的比较改成 而不是相加orz。
- 就这么简单了..接下来就看你基本功了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
freopen("cost.in","r",stdin);
freopen("cost.out","w",stdout);
}
const int MAXN=10010;
int n,m;
struct Edge{
int from,to,w;
bool operator <(const Edge x) const{
return w<x.w;
}
}edge[MAXN*21];
vector <Edge> edge1;
vector <int> G[MAXN];
int fa[MAXN];
int find_(int x){
if(x!=fa[x]) fa[x]=find_(fa[x]);
return fa[x];
}
void merge(int x,int y){
x=find_(x);
y=find_(y);
fa[y]=x;
}
void K(){
for (int i=1;i<=n;i++) fa[i]=i;
sort(edge+1,edge+m+1);
for (int i=1;i<=m;i++){
Edge e=edge[i];
int x=find_(e.from),y=find_(e.to);
if(x==y) continue;
merge(x,y);
edge1.push_back((Edge){e.from,e.to,e.w});
G[e.from].push_back(edge1.size()-1);
edge1.push_back((Edge){e.to,e.from,e.w});
G[e.to].push_back(edge1.size()-1);
}
}
bool visited[MAXN];
int father[MAXN][20],dist[MAXN][20],depth[MAXN];
void dfs(int u,int f,int deepth){
depth[u]=deepth;
father[u][0]=f;
visited[u]=true;
int siz=G[u].size();
for (int i=0;i<siz;i++){
Edge &e=edge1[G[u][i]];
int v=e.to;
if(!visited[v]){
dist[e.to][0]=e.w;
dfs(e.to,u,deepth+1);
}
}
}
void st_set(){
for (int j=1;j<=19;j++){
for (int i=1;i<=n;i++){
father[i][j]=father[father[i][j-1]][j-1];
dist[i][j]=max(dist[i][j-1],dist[father[i][j-1]][j-1]);
}
}
}
int q;
int Query(int x,int y){
if(depth[x]<depth[y])swap(x,y);
int ans=0;
for (int i=19;i>=0;i--){
if(depth[father[x][i]]>depth[y]){
ans=max(ans,dist[x][i]);
x=father[x][i];
}
}
for (int i=19;i>=0;i--){
if(father[x][i]!=father[y][i]){
ans=max(ans,max(dist[x][i],dist[y][i]));
x=father[x][i];
y=father[y][i];
}
}
ans=max(ans,max(dist[x][0],dist[y][0]));
return ans;
}
int main(){
fff();
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
edge[i].from=x,edge[i].to=y,edge[i].w=w;
}
K();
dfs(1,0,1);
st_set();
scanf("%d",&q);
while (q--){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",Query(x,y));
}
}