文章目录
1392A - Omkar and Password
相同为1,不同为n.
int T; qr(T); while(T--) {
qr(n); mn=inf; mx=-inf;
for(int i=1;i<=n;i++) qr(a[i]),mx=max(mx,a[i]),mn=min(mn,a[i]);
if(mx!=mn) puts("1");
else pr2(n);
}
1392B - Omkar and Infinity Clock
对 k k k的奇偶性讨论一下即可.
int n,a[N],mx,mn;
ll m;
int main() {
int T; qr(T); while(T--) {
qr(n); qr(m); mx=-inf; mn=inf;
for(int i=1;i<=n;i++) qr(a[i]),mx=max(mx,a[i]),mn=min(mn,a[i]);
if(m&1) for(int i=1;i<=n;i++) pr1(mx-a[i]);
else for(int i=1;i<=n;i++) pr1(a[i]-mn);
puts("");
}
return 0;
}
1392C - Omkar and Waterslide
贪心从后往前,局部最优解为全局最优解
int n,a[N],mx,mn;
ll m;
int main() {
int T; qr(T); while(T--) {
qr(n); ll ans=0;
for(int i=1;i<=n;i++) qr(a[i]);
for(int i=n;i>1;i--) {
ans += max(a[i-1]-a[i],0);
}
pr2(ans);
}
return 0;
}
1392D - Omkar and Bed Wars
比赛的时候知道可以dp,但是好像细节有一点点多就放弃了,现在觉得是非常蠢的抉择.
然后就想了一个贪心的做法.
首先我们仔细剖析一下题目的条件:在一个环上,每个点有出度,指向其相邻的点,如果一个对战局面合法,当且仅当 每个点的入度为2的倍数(RRL
,RRLL
) 或者 入度为1且和指向他的人对战(RL
)
对于一条单链LLL...L
或者RRR..RR
,显然我们每3个翻转一下即可(RRR
→
\rightarrow
→RRL
),最小操作数
c
e
i
l
(
n
/
3
)
ceil(n/3)
ceil(n/3).
否则就会有形如RRRLLRRRLLL
的情况,我们把每个RRRLLL
这种形态称为V
字.
显然一个V
字谷底能消化掉4个元素RRLL
,然后我们剩下的两边的元素各进行一遍单链上的操作即可(设V字分别有
n
,
m
n,m
n,m个R,L
,那么剩余
n
−
2
,
m
−
2
n-2,m-2
n−2,m−2个单链上的元素有待处理,左边的代价最小为
f
l
o
o
r
(
(
(
n
−
2
)
+
2
)
/
3
)
=
f
l
o
o
r
(
n
/
3
)
floor(((n-2)+2)/3)=floor(n/3)
floor(((n−2)+2)/3)=floor(n/3),右边类似).
问题在于有没有可能V字中R
链左端翻转去和左边的V字合并呢,答案是否定的.
因为如何翻转的话就一定要消耗1次,而在原链上是期望用1/3
次而已(总之一定
<
1
<1
<1,因为并不是每个元素都要翻转).
也就是说明这样的墙头草 弃暗投明 是不优的.
复杂度为 O ( n ) O(n) O(n).
贪心做法:
int n,a[N],sta[N],top;
char s[N];
ll m;
int main() {
int T; qr(T); while(T--) {
qr(n); scanf("%s",s+1);
int ans=0,sum=0;
for(int i=1;i<=n;i++) s[i]=(s[i]=='R'),sum+=s[i],s[i+n]=s[i];
if(!sum||sum==n) pr2((n+2)/3);
else {
int i=1;
for(i=1;i<=n;i++)
if(s[i+1]&&!s[i]) {i++; break;}
for(int j=i;j<=i+n-1;j++) s[j-i+1]=s[j];
sta[top=1]=1;
for(i=2;i<=n;i++)
if(s[i]==s[i-1]) sta[top]++;
else sta[++top]=1;
ans=0;
for(i=1;i<top;i+=2) {
ans += sta[i]/3+sta[i+1]/3;
}
if(i==top) ans += (sta[top]+2)/3;
pr2(ans);
}
}
return 0;
}
DP做法:
f
[
i
]
f[i]
f[i]表示对前
i
i
i 个位置进行处理的最小翻转次数.
我们考虑断环为链,然后翻转4次即可.
学习来源,
o
r
z
orz
orz
int n,ans,f[N];
char s[N];
int g(char *str,int l,int r) {return (str[l]!='R')+(str[r]!='L');}
int g(char *str,int x) {return (str[x]!='R')+(str[x+1]!='R')+(str[x+2]!='L')+(str[x+3]!='L');}
void dp(char *str) {
f[0]=0; f[1]=n;
for(int i=2;i<=n;i++) {
f[i]=f[i-2]+g(str,i-1,i);
if(i>=3) f[i]=min(f[i],f[i-3]+g(str,i-2,i));
if(i>=4) f[i]=min(f[i],f[i-4]+g(str,i-3));
}
ans=min(ans,f[n]);
}
int main() {
int T; qr(T); while(T--) {
qr(n); scanf("%s",s+1); ans=n;
for(int i=1;i<=4;i++) {
s[i+n]=s[i];
dp(s+i-1);
}
pr2(ans);
}
return 0;
}
1392E - Omkar and Duck
观察值域
v
≤
1
0
16
,
n
≤
25
v\le 10^{16},n\le 25
v≤1016,n≤25 .
从数据范围出发,
log
(
v
)
≥
2
n
\log(v)\ge 2n
log(v)≥2n,感觉一副可做的样子.
从弱化数据开始,假如是2*2的格子的话,我们只要左上右上分别为0,1即可明确路径.
类似地,只要我们每一步都知道要向下还是向右即可.
总共有
2
n
−
2
2n-2
2n−2步,所以我们可以每一个对角线赋一个二的次幂,然后01交替.
这样就可以还原啦~~
说实话,E应该是比D的贪心做法要简单一点的.
int n,q;
ll a[N][N];
int main() {
qr(n);
for(int i=1;i<=2*n-1;i++) {
ll k=1LL<<i; ll t=k;
for(int j=0;j<i;j++) a[j+1][i-j]=t,t^=k;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<n;j++) pr1(a[i][j]);
pr2(a[i][n]);
}
fflush(stdout);
qr(q); while(q--) {
ll t; qr(t);
for(int i=1,x=1,y=1;i<=2*n-1;i++) {
pr1(x); pr2(y);
if((t>>(i+1)&1)==(a[x+1][y]>0)) x++;
else y++;
}
fflush(stdout);
}
return 0;
}
另一种解法:
假设我们能使到达
(
i
,
j
)
(i,j)
(i,j)的路径的值都
>
(
i
+
1
,
j
−
1
)
>(i+1,j-1)
>(i+1,j−1)的,那么就可以还原.
特别的,我们令
r
o
w
=
1
,
c
o
l
=
n
row=1,col=n
row=1,col=n的都赋值为0,使值尽量的小.
然后就是一个简单的dp啦,求解的时候用个栈存一下即可.
这个方法的值域比较小----8 233 430 727 600
,比上面的做法优秀一点.
int n,q,sta[N*2][2],top;
ll s,a[N][N],f[N][N],g[N][N];
int main() {
qr(n);
for(int i=2;i<=n;i++) {
for(int j=1;j<n;j++) {
f[i][j]=g[i-1][j+1]+1;
a[i][j]=f[i][j]-f[i-1][j];
if(j==1) g[i][j]=g[i-1][j]+a[i][j];
else g[i][j]=g[i][j-1]+a[i][j];
}
a[i][n]=f[i][n]=0,g[i][n]=g[i][n-1];
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) pr1(a[i][j]);
puts("");
}
fflush(stdout); qr(q); while(q--) {
qr(s); int x=n,y=n; top=0;
while(x+y>=2) {
s -= a[x][y];
sta[++top][0]=x;
sta[top][1]=y;
if(x>1) {
if(s<=g[x-1][y]) x--;
else y--;
}
else y--;
}
while(top) {pr1(sta[top][0]); pr2(sta[top][1]); top--; }
fflush(stdout);
}
return 0;
}
1392F - Omkar and Landslide
可以发现同时滑落可以转化为顺序滑落,因为该滑过去的总要滑的.
假设
o
p
(
i
)
op(i)
op(i),表示从
i
i
i滑到
i
−
1
i-1
i−1,再滑到
i
−
2
i-2
i−2,直至不能滑动为止.
我们可以从前往后考虑,每次进行尽量多的
o
p
(
i
)
op(i)
op(i),可以发现序列每个时刻只会有至多一对相邻的数相同.(出现后,操作减少1时至多加1)
最后两个数要么相同要么差为1.
所以我们可以令序列初始值为0 1 2 .. (n-1)
,然后把剩下部分均分到
n
n
n项,前后的剩余分给前缀即可.
int n;
ll s,a[N];
int main() {
qr(n);
for(int i=1;i<=n;i++) qr(a[i]),s+=a[i]-i+1;
ll t=s/n; s%=n;
for(int i=1;i<=n;i++) pr1(i-1+t+(i<=s));
return 0;
}
1392G - Omkar and Pies
有两个长度为 k k k的01串 s , t s,t s,t,表示初始串和目标串.
有一个长度为 n n n的操作序列,每个操作为交换两个位置.
求一个操作区间 [ l , r ] , r − l + 1 ≥ m [l,r],r-l+1\ge m [l,r],r−l+1≥m(从左到右依次操作),使得两个串的公共部分最大.
显然,两个串进行完全相同的操作序列,那么结束后的公共部分和原来的相同.
我们令 s i s_i si 表示从 i i i 到 1 1 1 依次操作得到的结果序列,令 s 0 = s s_0=s s0=s. 同理设 t i t_i ti.
我们先进行 [ l , r ] [l,r] [l,r], 然后逆序操作 [ 1 , r ] [1,r] [1,r] ,得到的本质上就是 s l − 1 , t r s_{l-1},t_r sl−1,tr ,所以问题等价于求 s l − 1 , t r s_{l-1},t_r sl−1,tr 的匹配数.
咋求 s i s_i si呢? 我们已经得出了 s i − 1 s_{i-1} si−1 了,那么如果先进行 i i i 操作,那么 x i , y i x_i,y_i xi,yi 的位置就会掉换,所以我们维护一个 p o s [ x ] pos[x] pos[x] 表示原位置 x x x 的现在位置即可.
至于求匹配数,设 a , b , c a,b,c a,b,c分别为 s , t s,t s,t的1的个数和 ( s l − 1 , s r ) (s_{l-1},s_r) (sl−1,sr) 的公共1的数量,则有 a n s = c + [ k − ( a + b − c ) ] ans=c+[k-(a+b-c)] ans=c+[k−(a+b−c)].
我们定义 f [ i ] , g [ i ] f[i],g[i] f[i],g[i]分别表示 s s s二进制有 i i i的最小下标 和 t t t二进制有 i i i的最大下标,转移比较显然. 当 g [ i ] − f [ i ] ≥ m g[i]-f[i]\ge m g[i]−f[i]≥m时更新答案即可.
复杂度为 O ( n + 2 k ⋅ k ) O(n+2^k \cdot k) O(n+2k⋅k).
#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=1e6+10,M=22,size=1<<20,mod=998244353,inf=2e9;
//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
while(isdigit(c)) x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {
if(x<0)x=-x,putchar('-');
qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
if(x<0)x=-x,putchar('-');
qw(x); putchar('\n');
}
int n,m,k,s,cnt[1<<M],f[1<<M],g[1<<M],pos[M];
int in() {
static char ch[M];
scanf("%s",ch); int x=0;
for(int i=k-1;~i;i--) x=x*2+(ch[i]=='1');
return x;
}
void Swap(int &a,int x,int y) {
int u=a>>x&1,v=a>>y&1;
a &= ~((1<<x) | (1<<y));
a |= u<<y; a |= v<<x;
}
int main() {
qr(n); qr(m); qr(k); s=(1<<k)-1; cnt[0]=-1;
for(int i=0;i<=s;i++) f[i]=N,cnt[i]=cnt[i&(i-1)]+1;
int a=in(); int b=in();
for(int i=0;i<k;i++) pos[i]=i;
f[a]=0; g[b]=0;
// printf("a= %d\n",a);
// printf("b= %d\n",b);
for(int i=1;i<=n;i++) {
int x,y; qr(x); qr(y); x--; y--;
Swap(a,pos[x],pos[y]); Swap(b,pos[x],pos[y]); swap(pos[x],pos[y]);
f[a]=min(f[a],i);
g[b]=i;
// printf("a= %d\n",a);
// printf("b= %d\n",b);
}
pair<int,pair<int,int> > ans=mk(0,mk(0,0));
for(int i=s;~i;i--) {
if(g[i]-f[i]>=m)
ans=max(ans,mk(2*cnt[i]+(k-cnt[a]-cnt[b]),mk(f[i]+1,g[i])));
for(int j=0;j<k;j++) if(i>>j&1) {
int t=i^(1<<j);
f[t]=min(f[t],f[i]);
g[t]=max(g[t],g[i]);
}
}
pr2(ans.fi); pr1(ans.se.fi); pr2(ans.se.se); return 0;
}
1392H - ZS Shuffles Cards
概率题做的太少,毫无头绪.
看完大神的讲解,茅塞顿开…
如果我们称选到一个鬼牌并重洗算作一个迭代的话,那么如果我们可以求得 期望迭代次数 和 迭代期望选牌数,那么就可以求出总的期望次数啦.(每次迭代是各种情况的概率是相同的,与前面的情况无关).
迭代期望选牌数
{
E
(
x
)
=
1
⋅
m
n
+
m
+
2
⋅
n
n
+
m
m
n
+
m
−
1
+
3
⋅
n
n
+
m
n
−
1
n
+
m
−
1
m
n
+
m
−
2
.
.
.
.
.
.
=
∑
i
=
1
n
+
1
i
m
n
+
m
+
1
−
i
⋅
∏
j
=
0
i
−
2
n
−
j
n
+
m
−
j
\begin{cases}E(x)&=1\cdot \dfrac m {n+m} +2\cdot \dfrac{n}{n+m}\dfrac{m}{n+m-1}+3\cdot \dfrac{n}{n+m}\dfrac{n-1}{n+m-1}\dfrac{m}{n+m-2}...... \\&=\sum\limits_{i=1}^{n+1}\dfrac{im}{n+m+1-i} \cdot \prod\limits_{j=0}^{i-2} \dfrac {n-j}{n+m-j} \end{cases}
⎩⎪⎪⎨⎪⎪⎧E(x)=1⋅n+mm+2⋅n+mnn+m−1m+3⋅n+mnn+m−1n−1n+m−2m......=i=1∑n+1n+m+1−iim⋅j=0∏i−2n+m−jn−j
后面部分是可以递推的,所以复杂度为 O ( n ) O(n) O(n).
期望迭代次数
设
f
[
x
]
f[x]
f[x]表示有
x
x
x个数还不在
S
S
S时的期望迭代次数,显然
f
[
0
]
=
1
f[0]=1
f[0]=1(需要一次结束游戏)
那么可以得到
f
k
=
m
m
+
k
(
f
k
+
1
)
+
k
m
+
k
f
k
−
1
→
f
k
=
f
k
−
1
+
m
k
→
f
n
=
1
+
∑
i
=
1
n
m
i
f_k=\dfrac m {m+k} (f_k + 1) + \dfrac{k}{m+k} f_{k-1}\rightarrow f_k=f_{k-1}+\dfrac m k\rightarrow f_n=1+\sum_{i=1}^n \dfrac m i
fk=m+km(fk+1)+m+kkfk−1→fk=fk−1+km→fn=1+∑i=1nim.
总复杂度为 O ( n ) O(n) O(n).
ll n,m,s,inv[N],ans=1,w[N],E;
int main() {
qr(n); qr(m); s=n+m;
inv[0]=inv[1]=1; for(int i=2;i<=s;i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=n;i++) ans += m*inv[i]%mod;
ans=(ans%mod+mod)%mod; w[1]=1; E=m*inv[s]%mod;
for(int i=2;i<=n+1;i++) w[i]=w[i-1]*(n-i+2)%mod*inv[n+m-(i-2)]%mod,E += i*m%mod*inv[n+m+1-i]%mod*w[i]%mod;
E=(E%mod+mod)%mod; pr2(E*ans%mod); return 0;
}
上面的
E
(
x
)
E(x)
E(x)可以考虑组合意义更快的求解.
m
m
m张鬼牌把序列分成了
m
+
1
m+1
m+1个部分,每个数到每个部分的概率相等,所以期望有
n
m
+
1
\dfrac n{m+1}
m+1n 张数字牌在第一部分,有因为第一部分+一张鬼牌即表示
E
(
x
)
E(x)
E(x),所以
E
(
x
)
=
1
+
n
m
+
1
E(x)=1+\dfrac n {m+1}
E(x)=1+m+1n.
乌拉乌拉~~
ll n,m,s,inv[N],ans=1,E;
int main() {
qr(n); qr(m); s=max(n,m)+1;
inv[1]=1; for(int i=2;i<=s;i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=n;i++) ans += m*inv[i]%mod;
ans=(ans%mod+mod)%mod; E=1+n*inv[m+1]%mod; pr2(ans*E%mod); return 0;
}