Codeforces Global Round 10 部分题解

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 的奇偶性讨论一下即可.

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) .
否则就会有形如RRRLLRRRLLL的情况,我们把每个RRRLLL这种形态称为V字.
显然一个V字谷底能消化掉4个元素RRLL,然后我们剩下的两边的元素各进行一遍单链上的操作即可(设V字分别有 n , m n,m R,L,那么剩余 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) ,右边类似).

问题在于有没有可能V字中R链左端翻转去和左边的V字合并呢,答案是否定的.
因为如何翻转的话就一定要消耗1次,而在原链上是期望用1/3次而已(总之一定 < 1 <1 ,因为并不是每个元素都要翻转).
也就是说明这样的墙头草 弃暗投明 是不优的.

复杂度为 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] 表示对前 i i 个位置进行处理的最小翻转次数.
我们考虑断环为链,然后翻转4次即可.
学习来源, o r z 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 .
从数据范围出发, log ( v ) 2 n \log(v)\ge 2n ,感觉一副可做的样子.

从弱化数据开始,假如是2*2的格子的话,我们只要左上右上分别为0,1即可明确路径.
类似地,只要我们每一步都知道要向下还是向右即可.
总共有 2 n 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 + 1 , j 1 ) >(i+1,j-1) 的,那么就可以还原.
特别的,我们令 r o w = 1 , c o l = 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) ,表示从 i i 滑到 i 1 i-1 ,再滑到 i 2 i-2 ,直至不能滑动为止.
我们可以从前往后考虑,每次进行尽量多的 o p ( i ) op(i) ,可以发现序列每个时刻只会有至多一对相邻的数相同.(出现后,操作减少1时至多加1)
最后两个数要么相同要么差为1.
所以我们可以令序列初始值为0 1 2 .. (n-1),然后把剩下部分均分到 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 的01串 s , t s,t ,表示初始串和目标串.
有一个长度为 n n 的操作序列,每个操作为交换两个位置.
求一个操作区间 [ l , r ] , r l + 1 m [l,r],r-l+1\ge m (从左到右依次操作),使得两个串的公共部分最大.

显然,两个串进行完全相同的操作序列,那么结束后的公共部分和原来的相同.

我们令 s i s_i 表示从 i i 1 1 依次操作得到的结果序列,令 s 0 = s s_0=s . 同理设 t i t_i .

我们先进行 [ l , r ] [l,r] , 然后逆序操作 [ 1 , r ] [1,r] ,得到的本质上就是 s l 1 , t r s_{l-1},t_r ,所以问题等价于求 s l 1 , t r s_{l-1},t_r 的匹配数.

咋求 s i s_i 呢? 我们已经得出了 s i 1 s_{i-1} 了,那么如果先进行 i i 操作,那么 x i , y i x_i,y_i 的位置就会掉换,所以我们维护一个 p o s [ x ] pos[x] 表示原位置 x x 的现在位置即可.

至于求匹配数,设 a , b , c a,b,c 分别为 s , t s,t 的1的个数和 ( s l 1 , s r ) (s_{l-1},s_r) 的公共1的数量,则有 a n s = c + [ k ( a + b c ) ] ans=c+[k-(a+b-c)] .

我们定义 f [ i ] , g [ i ] f[i],g[i] 分别表示 s s 二进制有 i i 的最小下标 和 t t 二进制有 i i 的最大下标,转移比较显然. 当 g [ i ] f [ i ] m g[i]-f[i]\ge m 时更新答案即可.

复杂度为 O ( n + 2 k k ) O(n+2^k \cdot 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}

后面部分是可以递推的,所以复杂度为 O ( n ) O(n) .

期望迭代次数
f [ x ] f[x] 表示有 x x 个数还不在 S S 时的期望迭代次数,显然 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 .

总复杂度为 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) 可以考虑组合意义更快的求解.
m m 张鬼牌把序列分成了 m + 1 m+1 个部分,每个数到每个部分的概率相等,所以期望有 n m + 1 \dfrac n{m+1} 张数字牌在第一部分,有因为第一部分+一张鬼牌即表示 E ( x ) E(x) ,所以 E ( x ) = 1 + n m + 1 E(x)=1+\dfrac n {m+1} .

乌拉乌拉~~

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;
}

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/108051376