@happyforever
2018-11-05T14:26:41.000000Z
字数 7670
阅读 487
清北学堂 解题报告
期望得分:
实际得分:
概率,好不想做啊。。
算了随便写个方程想想转移。。。
首先考虑不打假赛是最优策略然后写
。。。(十五分钟后)
我转移写出来了????
真的假的
跑跑试试
它对了?
耶
写个挑几场打假赛然后扔上去吧
哇这个数据范围看着都发憷
分的背包还行
每次随机挑选几个物品看能凑成多少?
跑个次。。。
肯定能骗到分的吧qwq
那那个就删掉吧。。。
一场考试写两个随机算法真的是想日神仙吗
这这这这。。。
不就是随便维护吗
(敲完)
稳了稳了

改对之后

这就很难受了啊。。。。


/** 如果一直赢下去那么答案就是所有可能然后求出期望** dp[i][j]表示前i场赢了j场的概率* dp[i][j]=dp[i-1][j]*(1-p[i][j])+dp[i-1][j-1]*p[i][j-1];* 同时每次j循环开始前单独更新dp[i][0]=dp[i-1][0]*(1-p[i][0]);* 假设不打假赛那这样做就是结果*** f[i][j][k]表示前i场比赛赢了j场其中k场买外围** 但是这个做法过不了啊。。。。* 第一维滚动掉之后就开得下空间了,但是O(n^3)的。。。。* 就这边这破机子有点悬* 吔*** mmp* rand大法好*** 算了算了。。* 有T2用rand就够了* rp耗不起*/#include <cstdio>#include <cstring>const int N=1001;int n;double ans,p[N][N],dp[N][N];bool vis[N];int main(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;++i)for(int j=0;j<i;++j)scanf("%lf",&p[i][j]);if(n<=2){double emp=0;emp+=p[1][0]*p[2][1]*2;//两局都赢emp+=p[1][0]*(1-p[2][1])*1;//第一局赢,第二局输emp+=(1-p[1][0])*p[2][0]*1;//第一局输,第二局赢ans=emp;//第一局打假赛emp=p[2][0]*1;if(emp>ans)ans=emp;//第二局打假赛emp=p[1][0]*1;if(emp>ans)ans=emp;printf("%.2lf",ans);}else{// 骗分:假设所有比赛都不买外围是最优情况dp[0][0]=1;for(int i=1;i<=n;++i){dp[i][0]=dp[i-1][0]*(1-p[i][0]);for(int j=1;j<=i;++j)dp[i][j]=dp[i-1][j]*(1-p[i][j])+dp[i-1][j-1]*p[i][j-1];}for(int i=1;i<=n;++i)ans+=dp[n][i]*i;printf("%.2lf",ans);}fclose(stdin);fclose(stdout);return 0;}


/** 40分的01背包* 100分。。。可能是状压?* 鬼知道。** 我大概可以。。。维护一个大小的数组?* dfs枚举所有可能的组合?* 2^40次方。。* 。。。* 想屁吃。。。*** emmmm* 如果dfs可以剪枝掉非常多的情况,就dfs* 否则跑5e5次rand,rand出选择的物品* 所有rand出的情况取最优*/#include <cstdio>#include <cstdlib>#include <cstring>//#include <vector>#include <algorithm>const int N=41,M=1e4+1;int n,x,v[N],dp[M],ans;bool vis[N];//std::vector<int> qwq;void dfs(int now,int w){if(now>n){if(w==x){printf("0");exit(0);}if(w>ans)ans=w;return ;}// 先把所有都选上应该是个不错的选择。。// printf("%d %d\n",now,w);if(w+v[now]<=x)dfs(now+1,w+v[now]);dfs(now+1,w);}void work(){memset(vis,false,sizeof vis);int emp=0;for(int s,i=1;i<=n;++i){s=rand()%(n+1);vis[s]=true;}for(int i=1;i<=n;++i)if(vis[i]){if(emp+v[i]>x)continue ;emp+=v[i];}if(emp>ans)ans=emp;}inline int max(int x,int y){return x>y?x:y;}int main(){freopen("cake.in","r",stdin);freopen("cake.out","w",stdout);scanf("%d%d",&n,&x);for(int i=1;i<=n;++i)scanf("%d",&v[i]);/*for(int i=1;i<=100000;++i)work();printf("%d",x-ans);*/if(n<=20){for(int i=1;i<=n;++i)for(int j=x;j>=v[i];--j)dp[j]=max(dp[j],dp[j-v[i]]+v[i]);printf("%d",x-dp[x]);}else{// srand(time(0));// srand(19260817);srand(20010929);bool flag=false;for(int i=1;i<=n;++i){if(ans+v[i]<=x)ans+=v[i];else flag=true;}if(flag){std::sort(v+1,v+1+n);//排序对随机影响不大,但是需要在排序的基础上进行dfs剪枝效果的判断if(v[(n>>1)+1]>(x>>1))dfs(1,0);elsefor(int i=1;i<=500000;++i)//极限数据0.7s{if(ans==x){printf("0");goto E;}work();}}printf("%d",x-ans);}E: fclose(stdin);fclose(stdout);return 0;}


/** 10分输出m^(h*w)* 20分手玩*** 对于没有被限定的区域,其总方案数是m^(格子数)* 考虑每次询问实际是给一块矩形限制了上界,以及在最后时刻这个矩形内必须至少有一个是给定值* 考虑有一块矩形的上界都是v* 那么该矩形内的方案数为v^(格子数)-(v-1)^(格子数)* 把重叠的位置扣去,对每组询问记录其格子数*** 突然发现调出来之后如果不删输出调试的注释,代码好丑陋啊。*/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e4+1,mod=1e9+7;int h,w,m,n,map[N][N];struct Node{int a,b,x,y,v,sum;bool operator<(const Node &gg)const{return v<gg.v;}}q[11];inline int ksm(long long x,int b){long long res=1;for(;b;b>>=1,x=x*x%mod)if(b&1)res=res*x%mod;return res;}int main(){freopen("grid.in","r",stdin);freopen("grid.out","w",stdout);h=read(),w=read(),m=read(),n=read();int sum=h*w;for(int i=1;i<=h;++i)for(int j=1;j<=w;++j)map[i][j]=m+1;for(int i=1;i<=n;++i){q[i]=(Node){read(),read(),read(),read(),read(),0};q[i].sum=(q[i].b-q[i].a+1)*(q[i].y-q[i].x+1);//数据保证x>a,y>b}// puts("GG");// 离线处理std::sort(q+1,q+1+n);for(int v,k=1;k<=n;++k){v=q[k].v;for(int i=q[k].a;i<=q[k].x;++i){for(int j=q[k].b;j<=q[k].y;++j){// printf("%d ",map[i][j]);if(map[i][j]>v)map[i][j]=v,--sum;else --q[k].sum;}// puts("");}}// printf("%d",sum);int ans=ksm(m,sum);//没有被限制的区域的方案数for(int emp,i=1;i<=n;++i){// printf("%d %d %d\n",emp,q[i].v,q[i].sum);emp=(ksm(q[i].v,q[i].sum)-ksm(q[i].v-1,q[i].sum)+mod)%mod;ans=ans*emp%mod;}printf("%d",ans);fclose(stdin);fclose(stdout);return 0;}
最优策略就是不卖外围不打假赛,每一场都认认真真打
然后就期望最大了。。。
它范围和时限开那么大是误导人用的。。
太毒瘤了这人。
证明:最优策略就是认真打比赛
假设有这样一个状态
已经赢了场
在状态获胜的概率为状态获胜概率为状态获胜概率为
如果这场比赛获胜了那么会转移到,否则会转移到
考虑如果这场比赛打假赛那么最后期望会增加
如果这场比赛认真打那么会有几种情况:状态赢状态赢,状态赢状态输,状态输状态赢,状态输状态输
分别对应的期望是
不妨假设
那么这场比赛打假赛会增加的期望,这场比赛认真打会增加的期望
化简出来是
表示前场赢了场的概率
当然需要单独转移:
最后
#include <cstdio>#include <cstring>const int N=1001;int n;double ans,p[N][N],dp[N][N];bool vis[N];int main(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;++i)for(int j=0;j<i;++j)scanf("%lf",&p[i][j]);dp[0][0]=1;for(int i=1;i<=n;++i){dp[i][0]=dp[i-1][0]*(1-p[i][0]);for(int j=1;j<=i;++j)dp[i][j]=dp[i-1][j]*(1-p[i][j])+dp[i-1][j-1]*p[i][j-1];}for(int i=1;i<=n;++i)ans+=dp[n][i]*i;printf("%.2lf",ans);fclose(stdin);fclose(stdout);return 0;}
折半搜索
虽然无法接受,但是还是比较稳的
把所有物品分成两堆,第一堆有个,第二堆是剩下的那些
那么对这两堆物品分别求出能够组合出的价值和
考虑Meet in the middle
排序后用两个指针从两侧向中间夹逼找到最接近的和,或者在右边的一堆有一个,在左边的一堆里面找出小于等于的最大值
#include <cstdio>#include <algorithm>const int N=1<<20|1,M=1e4+1;int x,n,up,a[N],v[41],ans;void dfs1(int now,int w){if(now>up){a[++a[0]]=-w;// printf("%d\n",w);return ;}if(w+v[now]<=x)dfs1(now+1,w+v[now]);dfs1(now+1,w);}int emp,tmp;void dfs2(int now,int w){if(now>n){// printf("%d\n",w);emp=w-x;if(emp<=a[a[0]])w-=a[std::lower_bound(a+1,a+1+a[0],emp)-a];if(w>ans)ans=w;return ;}if(w+v[now]<=x)dfs2(now+1,w+v[now]);dfs2(now+1,w);}int main(){freopen("cake.in","r",stdin);freopen("cake.out","w",stdout);scanf("%d%d",&n,&x);up=n>>1;for(int i=1;i<=n;++i)scanf("%d",&v[i]);dfs1(1,0);std::sort(a+1,a+1+a[0]);dfs2(up+1,0);printf("%d",x-ans);fclose(stdin);fclose(stdout);return 0;}
对当前的图考虑有个矩形将其覆盖
对于没有被任何矩形覆盖的位置,假设其个数为,那么这部分没有被任何矩形覆盖的位置对答案的贡献为
而对于被矩形覆盖过的位置,其实质是将这些位置的值域限制在了“最大值”
那么离线之后每个位置的值域就会变成所有覆盖到这个位置的矩形的的最小值
考虑每个矩形对答案的贡献:假设矩形大小等于,那么对于这个矩形,假设忽略其“矩形内至少有一个值等于”的限制,那么总共有种构造方法,再减掉其中不合法的(没有任何一个位置的值等于)方案数
另外由于有的矩形会重合,所以需要对某些矩形扣掉一部分
这里给出一种离线方法:
注意到每个位置最后保留的都是覆盖过该位置的最小的矩阵的
首先把所有的操作都读入,读入时记录该操作覆盖的格子数;将所有操作按照从小到大排序,每次操作暴力枚举每个位置进行覆盖;如果枚举到了某个之前有值的位置,那么说明这个位置需要从当前矩阵中扣出,也就是当前矩阵的
考虑无解的情况:某两个矩形是包含关系且被包含的矩形的要大于外面包含它的矩形的
另外这题需要二维离散化。。。。。
下面给出没有二维离散化过的代码(会个点,因为复杂度是的)
#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e4+1,mod=1e9+7;int h,w,m,n,map[N][N];struct Node{int a,b,x,y,v,sum;bool operator<(const Node &gg)const{return v==gg.v?sum<gg.sum:v<gg.v;}}q[11];inline int ksm(long long x,int b){long long res=1;for(;b;b>>=1,x=x*x%mod)if(b&1)res=res*x%mod;return res;}int main(){freopen("grid.in","r",stdin);freopen("grid.out","w",stdout);h=read(),w=read(),m=read(),n=read();int sum=h*w;for(int i=1;i<=n;++i){q[i]=(Node){read(),read(),read(),read(),read(),0};q[i].sum=(q[i].x-q[i].a+1)*(q[i].y-q[i].b+1);}std::sort(q+1,q+1+n);for(int v,k=1;k<=n;++k){v=q[k].v;for(int i=q[k].a;i<=q[k].x;++i){for(int j=q[k].b;j<=q[k].y;++j){if(map[i][j]>v || map[i][j]==0)map[i][j]=v,--sum;else --q[k].sum;}}}long long emp,ans=ksm(m,sum);for(int i=1;i<=n;++i){if(!q[i].sum)continue;emp=(ksm(q[i].v,q[i].sum)-ksm(q[i].v-1,q[i].sum)+mod)%mod;ans=ans*emp%mod;}printf("%d",ans);fclose(stdin);fclose(stdout);return 0;}