文章目录
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
对 的奇偶性讨论一下即可.
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
RRL
),最小操作数
.
否则就会有形如RRRLLRRRLLL
的情况,我们把每个RRRLLL
这种形态称为V
字.
显然一个V
字谷底能消化掉4个元素RRLL
,然后我们剩下的两边的元素各进行一遍单链上的操作即可(设V字分别有
个R,L
,那么剩余
个单链上的元素有待处理,左边的代价最小为
,右边类似).
问题在于有没有可能V字中R
链左端翻转去和左边的V字合并呢,答案是否定的.
因为如何翻转的话就一定要消耗1次,而在原链上是期望用1/3
次而已(总之一定
,因为并不是每个元素都要翻转).
也就是说明这样的墙头草 弃暗投明 是不优的.
复杂度为 .
贪心做法:
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做法:
表示对前
个位置进行处理的最小翻转次数.
我们考虑断环为链,然后翻转4次即可.
学习来源,
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
观察值域
.
从数据范围出发,
,感觉一副可做的样子.
从弱化数据开始,假如是2*2的格子的话,我们只要左上右上分别为0,1即可明确路径.
类似地,只要我们每一步都知道要向下还是向右即可.
总共有
步,所以我们可以每一个对角线赋一个二的次幂,然后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;
}
另一种解法:
假设我们能使到达
的路径的值都
的,那么就可以还原.
特别的,我们令
的都赋值为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
可以发现同时滑落可以转化为顺序滑落,因为该滑过去的总要滑的.
假设
,表示从
滑到
,再滑到
,直至不能滑动为止.
我们可以从前往后考虑,每次进行尽量多的
,可以发现序列每个时刻只会有至多一对相邻的数相同.(出现后,操作减少1时至多加1)
最后两个数要么相同要么差为1.
所以我们可以令序列初始值为0 1 2 .. (n-1)
,然后把剩下部分均分到
项,前后的剩余分给前缀即可.
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
有两个长度为 的01串 ,表示初始串和目标串.
有一个长度为 的操作序列,每个操作为交换两个位置.
求一个操作区间 (从左到右依次操作),使得两个串的公共部分最大.
显然,两个串进行完全相同的操作序列,那么结束后的公共部分和原来的相同.
我们令 表示从 到 依次操作得到的结果序列,令 . 同理设 .
我们先进行 , 然后逆序操作 ,得到的本质上就是 ,所以问题等价于求 的匹配数.
咋求 呢? 我们已经得出了 了,那么如果先进行 操作,那么 的位置就会掉换,所以我们维护一个 表示原位置 的现在位置即可.
至于求匹配数,设 分别为 的1的个数和 的公共1的数量,则有 .
我们定义 分别表示 二进制有 的最小下标 和 二进制有 的最大下标,转移比较显然. 当 时更新答案即可.
复杂度为 .
#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
概率题做的太少,毫无头绪.
看完大神的讲解,茅塞顿开…
如果我们称选到一个鬼牌并重洗算作一个迭代的话,那么如果我们可以求得 期望迭代次数 和 迭代期望选牌数,那么就可以求出总的期望次数啦.(每次迭代是各种情况的概率是相同的,与前面的情况无关).
迭代期望选牌数
后面部分是可以递推的,所以复杂度为 .
期望迭代次数
设
表示有
个数还不在
时的期望迭代次数,显然
(需要一次结束游戏)
那么可以得到
.
总复杂度为 .
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;
}
上面的
可以考虑组合意义更快的求解.
张鬼牌把序列分成了
个部分,每个数到每个部分的概率相等,所以期望有
张数字牌在第一部分,有因为第一部分+一张鬼牌即表示
,所以
.
乌拉乌拉~~
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;
}