AtCoder Beginner Contest 189
A
…
B
…
C
n n n 个竖着的条,高度为 a i a_i ai 宽度为 1 1 1 ,在内部选择一个矩形,求其最大面积
由于面积要尽量大,最后选择的矩形肯定会在某处卡到上界,枚举在哪里卡到上界
单调栈预处理每个位置向左向右分别能扩展到的最远位置,然后计算答案
#include <bits/stdc++.h>
using namespace std;
typedef vector <int> vi;
#define pb push_back
const int N = 1500005;
int l[N],r[N],a[N],n;
int main()
{
cin>>n;for (int i=1;i<=n;++i) scanf("%d",&a[i]);
vi vc;
a[0]=a[n+1]=0;
vc.clear();vc.pb(0);
for (int i=1;i<=n;++i)
{
while (a[i]<=a[vc.back()]) vc.pop_back();
l[i]=vc.back()+1;
vc.push_back(i);
}
vc.clear();vc.pb(n+1);
for (int i=n;i;--i)
{
while (a[i]<=a[vc.back()]) vc.pop_back();
r[i]=vc.back()-1;
vc.push_back(i);
}
int ans=0;
for (int i=1;i<=n;++i) ans=max(ans,a[i]*(r[i]-l[i]+1));
cout<<ans<<endl;
return 0;
}
D
给出 N N N 个运算符(逻辑与或者逻辑或),问有多少个数列 X 0 , X 1 , . . . X n X_0,X_1,...X_n X0,X1,...Xn 满足 ( ( ( X 0 S 1 X 1 ) S 2 X 2 ) . . . ) S n X n (((X_0 S_1 X_1)S_2 X_2)...)S_n X_n (((X0S1X1)S2X2)...)SnXn 的取值为真
d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1] 表示进行第 i i i 次运算后结果为 0 / 1 0/1 0/1 , X 0 , X 1 , . . . , X i X_0,X_1,...,X_i X0,X1,...,Xi 的方案数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1500005;
ll dp[N][2];
string op[N];
int n;
int main()
{
cin>>n;for (int i=1;i<=n;++i) cin>>op[i];
dp[0][0]=dp[0][1]=1;
for (int i=0;i<n;++i)
for (int j=0;j<2;++j)
for (int k=0;k<2;++k)
{
int nw=0;
if (op[i+1][0]=='A') nw=j&k;else nw=j|k;
dp[i+1][nw]+=dp[i][j];
}
cout<<dp[n][1]<<endl;
return 0;
}
E
给出n个点,m次操作,q次询问,问第 a 次操作后第 b 个点的坐标
四个操作都可以写成矩阵乘法的形式,记录矩阵的前缀积即可
操作一
[ x y 1 ] [ 0 − 1 0 1 0 0 0 0 1 ] = [ y − x 1 ] \begin{bmatrix} x & y & 1 \end{bmatrix} \begin{bmatrix} 0 & -1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} y & -x & 1 \end{bmatrix} [xy1]⎣⎡010−100001⎦⎤=[y−x1]
操作二
[ x y 1 ] [ 0 1 0 − 1 0 0 0 0 1 ] = [ − y x 1 ] \begin{bmatrix} x & y & 1 \end{bmatrix} \begin{bmatrix} 0 & 1 & 0 \\ -1 & 0 & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} -y & x & 1 \end{bmatrix} [xy1]⎣⎡0−10100001⎦⎤=[−yx1]
操作三
[ x y 1 ] [ − 1 0 0 0 1 0 2 t 0 1 ] = [ 2 t − x y 1 ] \begin{bmatrix} x & y & 1 \end{bmatrix} \begin{bmatrix} -1 & 0 & 0 \\ 0 & 1 & 0 \\ 2t & 0 & 1 \end{bmatrix} = \begin{bmatrix} 2t-x & y & 1 \end{bmatrix} [xy1]⎣⎡−102t010001⎦⎤=[2t−xy1]
操作四
[ x y 1 ] [ 1 0 0 0 − 1 0 0 2 t 1 ] = [ x 2 t − y 1 ] \begin{bmatrix} x & y & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 2t & 1 \end{bmatrix} = \begin{bmatrix} x & 2t-y & 1 \end{bmatrix} [xy1]⎣⎡1000−12t001⎦⎤=[x2t−y1]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N = 1500005;
struct Matrix
{
const static int N = 5;
const static int n = 3;
ll x[N][N];
void print()
{
for (int i=1;i<=n;++i)
{
for (int j=1;j<=n;++j) cout<<x[i][j]<<' ';
cout<<endl;
}
}
void clear() {memset(x,0,sizeof(x));};
void idtt() {clear();for (int i=1;i<=n;++i) x[i][i]=1;};
friend Matrix operator * (const Matrix a,const Matrix b)
{
Matrix res;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
{
res.x[i][j]=0;
for (int k=1;k<=n;++k)
{
res.x[i][j]=(res.x[i][j]+a.x[i][k]*b.x[k][j]);
}
}
return res;
}
}a[N],s[N];
int n,m,Q;
int main()
{
s[0].idtt();
cin>>n;
for (int i=1,x,y;i<=n;++i)
{
scanf("%d%d",&x,&y);
a[i].clear();
a[i].x[1][1]=x;a[i].x[1][2]=y;a[i].x[1][3]=1;
}
cin>>m;
for (int i=1,p,t;i<=m;++i)
{
s[i].clear();
cin>>p;
if (p==1)
{
s[i].x[1][2]=-1;s[i].x[2][1]=1;s[i].x[3][3]=1;
continue;
}
if (p==2)
{
s[i].x[1][2]=1;s[i].x[2][1]=-1;s[i].x[3][3]=1;
continue;
}
cin>>t;
if (p==3)
{
s[i].x[1][1]=-1;s[i].x[2][2]=1;s[i].x[3][1]=t*2;s[i].x[3][3]=1;
continue;
}
if (p==4)
{
s[i].x[1][1]=1;s[i].x[2][2]=-1;s[i].x[3][2]=t*2;s[i].x[3][3]=1;
continue;
}
assert(0);
}
for (int i=1;i<=m;++i) s[i]=s[i-1]*s[i];
cin>>Q;
for (int cas=1,ti,id;cas<=Q;++cas)
{
scanf("%d%d",&ti,&id);
Matrix t=a[id]*s[ti];
printf("%lld %lld\n",t.x[1][1],t.x[1][2]);
}
return 0;
}
F
n+1个点,k个陷阱,起点为0
每轮随机从1~m中抽一个数并走这么多步,当走完后停留在陷阱上时会传送回0号点
当走到或超过n号点时结束
问期望进行几轮
考虑dp[i]为从0走到i的期望步数,从前往后推
发现非常复杂,因为有往回走的情况,dp值很难处理
再试试从后往前推,考虑dp[i]为从i走到超过或等于n的点的期望步数
假设当前在i处,是个坑
那么会被传送回0处,所以dp[i]=dp[0]
假设当前在i处,不是坑,要走到j处,分两种情况
若j不是坑: d p [ i ] = d p [ i ] + 1 m ( d p [ j ] + 1 ) dp[i]=dp[i]+\frac 1 m (dp[j]+1) dp[i]=dp[i]+m1(dp[j]+1)
若j是坑: d p [ i ] = d p [ i ] + 1 m ( d p [ 0 ] + 1 ) dp[i]=dp[i]+\frac 1 m (dp[0]+1) dp[i]=dp[i]+m1(dp[0]+1)
由于坑的dp值和0的dp值相同,所以上面两个情况可以用同一个方程
综上,若 i i i 是坑,有 d p [ i ] = d p [ 0 ] dp[i]=dp[0] dp[i]=dp[0]
若
i
i
i 不是坑,则有
d
p
[
i
]
=
1
+
1
m
∑
j
=
i
+
1
i
+
m
d
p
[
j
]
dp[i]=1+\frac 1 m \sum _{j=i+1}^{i+m} dp[j]
dp[i]=1+m1j=i+1∑i+mdp[j]
那么问题来了,坑的dp值与0的dp值相同,但推到坑时还不知道0处的dp值是多少
考虑将 d p [ 0 ] dp[0] dp[0] 视为未知数,不影响dp过程,将所有dp值用 d p [ 0 ] ∗ x + y dp[0]*x+y dp[0]∗x+y 的形式代替,只记录 ( x , y ) (x,y) (x,y)
这样推到0时就会有 d p [ 0 ] = d p [ 0 ] ∗ x + y dp[0]=dp[0]*x+y dp[0]=dp[0]∗x+y ,解方程即可
#include <bits/stdc++.h>
using namespace std;
typedef long double ldb;
#define pb push_back
const int N = 1500005;
struct pldld
{
ldb x,y;
friend pldld operator + (const pldld x,const ldb y)
{
return {x.x,x.y+y};
}
friend pldld operator + (const pldld x,const pldld y)
{
return {x.x+y.x,x.y+y.y};
}
friend pldld operator - (const pldld x,const pldld y)
{
return {x.x-y.x,x.y-y.y};
}
friend pldld operator / (const pldld x,const ldb y)
{
return {x.x/y,x.y/y};
}
}dp[N],s[N];
int a[N],flag[N];
int n,m,k;
int main()
{
cin>>n>>m>>k;
for (int i=1;i<=k;++i) cin>>a[i],flag[a[i]]=1;
for (int i=n-1;i>=0;--i)
{
if (flag[i]) dp[i]={1,0};else
{
dp[i]=((s[i+1]-s[i+m+1])/m)+1;
}
s[i]=s[i+1]+dp[i];
}
if ((1-dp[0].x)<1e-9) {puts("-1");return 0;};
ldb ans=dp[0].y/(-dp[0].x+1);
printf("%.10f\n",(double)ans);
return 0;
}