2021.09.05 膜你赛
T1 game
Description
\(Ariel\) 最近去参加了一个锦标赛,这个锦标赛总共有 \(n\) 轮比赛,最终成绩由这 \(n\) 轮比赛中赢的轮数决定。对于 \(Ariel\) 每一轮比赛的胜利概率,则取决于他在该轮比赛之前的战绩。也就是说,如果 \(Ariel\) 在第 \(i\) 轮比赛选择积极应战,并且前 \(i-1\) 轮比赛中取得了 \(j\) 胜的话,那么第 \(i\) 轮比赛的胜率概率为 \(p[i][j]\),这里我们保证了一点就是对于同一个 \(i\),\(p[i][j]\) 关于 \(j\) 的上升保持单调不上升(也就是说 \(p[i][j] \geq p[i][j+1])\)。
\(Ariel\) 观察到这个规则之后,想到了一个可能可以使他最终成绩更优的方法,就是在某些轮比赛采取第二种策略,故意求败,也就是以 \(100\%\) 的概率输掉该轮比赛,从而使自己在后面能够遇到更容易对付的对手。
\(Ariel\) 现在已经看到了整个 \(p\) 数组,希望你能告诉他一个最优的策略,使得他能最大化他的期望赢的轮数。这里,定义一下期望。假如我们要求一个事件 \(A\) 的期望,那么假如事件 \(A\) 以 \(P_i\) 的概率结果为 \(i\),那么事件 \(A\)
的期望则是 \(i\times P_i\) 的和,大概的含义就是结果值关于概率的一个加权平均数。
Solution
不明白怎么做最优决策,于是直接根据样例莽上去的概率期望dp。
设 \(f_{i,j}\) 表示当前在第 \(i\) 轮,赢 \(j\) 轮的概率。
\(f_{i,j}=f_{i-1,j}\times(i-p_{i,j})+f_{i-1,j-1}\times p_{i,j-1}\)
最后答案为 \(\sum\limits_{i=1}^n i\cdot f_{n,i}\) 。
/*
* @Author: smyslenny
* @Date: 2021.09.
* @Title:
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
double p[M][M],f[M][M];
namespace substack1{
double Ans;
void main()
{
if(n==1) printf("%.2lf\n",p[1][0]);
else
{
Ans+=(1-p[1][0])*(1-p[2][0])*0+(1-p[1][0])*p[2][0]+p[1][0]*(1-p[2][1])+p[1][0]*p[2][1]*2;
printf("%.2lf\n",Ans);
}
}
}
namespace substack2{
double Ans;
void main()
{
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++)
f[i][j]+=(j<i)?f[i-1][j]*(1-p[i][j]):0.0,//这轮没连续赢,才有输的概率
f[i][j]+=(j>0)?f[i-1][j-1]*p[i][j-1]:0.0;//这轮没连续输,才有赢的概率
for(int i=0;i<=n;i++)
Ans+=f[n][i]*i;
printf("%.2lf",Ans);
}
}
int main()
{
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
scanf("%lf",&p[i][j]);
if(n<=2) substack1::main();
else substack2::main();
return 0;
}
/*
2
0.5
0.5 0.5
2
0.5
0.4 0.3
*/
T2
Description
双十一就要来啦!\(YCC\) 刚刚获得了一笔 \(X\) 元的奖金。那么是不是应该清空下购物车呢?
购物车总共有 \(N\) 个物品,每个物品的价格为 \(V_i\),\(YCC\) 想尽可能地把手头的奖金给花光,所以她要精新选择一些商品,使得其价格总和最接近但又不会超过奖金的金额。那么 \(YCC\) 最后最少可以剩下多少钱呢?
Solution
一开始写的dp,先造了几组数据发现没问题,然后就去做别的题了,后来写了暴力去拍发现写假了,想不出怎么改,交上去了暴力,发现只要加一句剪枝就过了。
Code
/*
* @Author: smyslenny
* @Date: 2021.09.05
* @Title: cake
* @Main idea:dp
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n,V,v[M];
int f[M][3];
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
bool cmp(int a,int b)
{
return a>b;
}
int js,Ans,fg;
namespace substack2{
void main()
{
sort(v+1,v+1+n,cmp);
if(v[1]<=V) f[1][1]=v[1];
else f[1][1]=0;
f[1][0]=0;
for(int i=2;i<=n;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);
if(f[i-1][0]+v[i]<=V)
f[i][1]=max(f[i][1],f[i-1][0]+v[i]);
if(f[i-1][1]+v[i]<=V)
f[i][1]=max(f[i][1],f[i-1][1]+v[i]);
}
printf("%lld\n",V-max(f[n][0],f[n][1]));
}
}
namespace substack1
{
void dfs(int x)
{
// if(fg) return;
// if(js<0) {fg=1;return;}
if(js>V) return;
if(x>n) return;
if(js+v[x]<=V)
{
js+=v[x];
Ans=max(Ans,js);
dfs(x+1);
js-=v[x];
}
dfs(x+1);
}
void main()
{
if(n>1000)
substack2::main();
else
{
dfs(1);
printf("%lld\n",V-Ans);
}
}
}
namespace substack3{
int sum[M],Ans;
void dfs(int x)
{
if(js+sum[x]<Ans) return;
if(js>V) return;
if(x>n) return;
if(js+v[x]<=V)
{
js+=v[x];
Ans=max(Ans,js);
dfs(x+1);
js-=v[x];
}
dfs(x+1);
return;
}
void main()
{
sort(v+1,v+1+n,cmp);
for(int i=n;i>=1;i--) sum[i]=sum[i+1]+v[i];
dfs(1);
printf("%lld\n",V-Ans);
}
}
signed main()
{
// freopen("cake.in","r",stdin);
// freopen("cake.out","w",stdout);
n=read(),V=read();
for(int i=1;i<=n;i++)
v[i]=read();
substack1::main();
return 0;
}
/*
6 2000
200 700 600 1900 300 400
*/
T3
Description
现在轮到 \(BS\) 给这题造数据了,但他又不会写这题的标程,于是对于每个询问他都先随机出一个答案,接着想通过答案去构造出一个满足所有答案的数据。换句话说,对于构造出来的矩阵,对于每次询问的子矩阵,其中的最大值需要等于 \(BS\) 预先设定的答案。
现在 \(BS\) 已经预先设定好答案了,那么满足要求的矩阵到底有多少个呢?
Solution
对于一个点来说,他能做出贡献的只有覆盖在它上面的需要的最大值最小的矩形,易知对于只有一个矩形的图,答案为 \((m^{cnt}-(m-1)^{cnt})\times m^{k\times m-cnt}\) ,然后因为矩形之后十个,对于每个点,存一个十位的状态表示他对这些矩形是否做出贡献,对一个矩形,强制让这个点做贡献+非强制做贡献,dp一下,但是 $w\times h $ 个点非常大,考虑离散化。
好像没过,以后再改吧。
Code
/*
* @Author: smyslenny
* @Date: 2021.09.05
* @Title:
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=1e9+7;
const int M=1e3+5;
int T,n,m,h,w;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
int qx[M],x,qy[M],y,Ans,f[M][M];
struct node{
int x_1,y_1,x_2,y_2,v;
}sz[M];
int qpow(int a,int b) {
int ans=1;
while(b) { if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1; }
return ans;
}
int get_one(int x)
{
int js=0;
while(x)
{
if(x&1) js++;
x>>=1;
}
return js;
}
int main()
{
T=read();
while(T--) {
n=read(),m=read(),h=read(),w=read(),Ans=0,x=0,y=0;
for(int i=1;i<=n;i++)
sz[i].x_1=read(),sz[i].y_1=read(),sz[i].x_2=read(),sz[i].y_2=read(),sz[i].v=read(),
qx[++x]=sz[i].x_1,qx[++x]=sz[i].x_2,qy[++y]=sz[i].y_1,qy[++y]=sz[i].y_2;
qx[++x]=1,qx[++x]=h+1,qy[++y]=1,qy[++y]=w+1;
// init();
sort(qx+1,qx+1+x);
x=unique(qx+1,qx+1+x)-qx-1;
sort(qy+1,qy+1+y);
y=unique(qy+1,qy+1+y)-qy-1;
for(int i=0;i<(1<<n);i++)
{
for(int j=1;j<x;j++)
for(int k=1;k<y;k++)
f[j][k]=m;
for(int j=0;j<=n;j++)
for(int k=lower_bound(qx+1,qx+1+x,sz[j].x_1)-qx;qx[k]!=sz[j].x_2;k++)
for(int l=lower_bound(qy+1,qy+1+y,sz[j].y_1)-qy;qy[l]!=sz[j].y_2;l++)
f[k][l]=min(f[k][l],sz[j].v-(i>>(j-1)&1));
int res=1;
for(int j=1;j<x;j++)
for(int k=1;k<y;k++)
res=res*qpow(f[j][k],(qx[j+1]-qx[j])*(qy[j+1]-qy[j]));
Ans=((Ans+get_one(i)&1?-1:1+mod)*res%mod)%mod;
}
printf("%d\n",Ans);
}
return 0;
}