我的隔天Codeforces——Round #370 (Div. 2)

好久没有做Codeforces了,今天心血来潮做了一发。我觉得我缺少解题时灵活变通的能力,明明好多题目我是会的,但是有的时候想不到那个梗(不会变通,转化等价题意),错失了机会。我觉得这个能力需要提升的话,一个必须多刷题目,多多见识各种各样的思路,还有就是应该多参加一些(大的小的)比赛。所以,在退役之前(或者一直到自己找到实习单位)多多参加各种各样的比赛来锻炼自己。

A. Memory and Crow

分析:签到题嘛,比较简单。根据给出的公式,可以推导出 bi=ai+ai+1,1i<n bn=an

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          100000+10
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

/*inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

//--------------------------------------------------

int dat[maxn];
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&dat[i]);
    for(int i=0;i<n-1;i++)
        dat[i]+=dat[i+1];
    int cnt=0;
    for(int i=0;i<n;i++){
        if(cnt)putchar(' ');
        cnt=1;
        printf("%d",dat[i]);
    }
    puts("");
    return 0;
}


B. Memory and Trident

分析:同签到题。U和D是一对,不管他们出现在字符串的那里,都要相互抵消,L和R一样。那么,分别求出L与R的差值,U和D的差值,相加除以2,结果就是需要变化的最小步数。

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          100000+10
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

/*inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

//--------------------------------------------------

char dat[maxn];
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    scanf("%s",dat);
    int len=strlen(dat);
    if(len&1){
        puts("-1");
        return 0;
    }
    int a=0,b=0;
    for(int i=0;i<len;i++)
        if(dat[i]=='U')a++;
        else if(dat[i]=='D')a--;
        else if(dat[i]=='L')b++;
        else b--;
    a=abs(a),b=abs(b);
    printf("%d\n",(a+b)/2);
    return 0;
}


C. Memory and De-Evolution

分析:这应该不算签到了,应该算一道简单模拟。将(x,x,x)转换到(y,y,y),那么类似的可以反过来考虑将(y,y,y)转化到(x,x,x)。我们尽量要快速变化,那么变化最小边就是合理的选择(如果变化其他的边,变化的跨度就小,如果变化本来就小的边,那么变化的跨度就会很大)。每次将最小的边尽可能的变大,就是变成次大边和最大边的和减一,但是又不能比目标大,如果大于目标只能改成等于目标。我们发现这样是以斐波那契的速度向大数推进,基本只需要30+步就可以从3推到100000,复杂度没有问题。

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          100000+10
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

/*inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

//--------------------------------------------------


int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int a,b,c,x,y;
    scanf("%d %d",&y,&x);
    a=b=c=x;
    int num=0;
    do{
        num++;
        a=(b+c)-1;
        if(a>y)a=y;
        swap(a,c);
        swap(a,b);
    }while(!(a==b&&b==c));
    printf("%d\n",num);
    return 0;
}


D. Memory and Scores

分析:这是一道比较难的题目,需要重点说下。

题意:Memory和Lexa玩拼点游戏,已知Memory开始有a点,Lexa开始有b点。游戏一共有t轮,每轮每个人在[-k,k]之间选择一个整数加到自己的点数上。问最后Memory的点数大于Lexa的点数有多少种可能?

做法:一开始确实想到了DP,而且还尝试着写了一下。但是因为写法太烂了,100左右的数据就TLE了。看了一下别人的代码用到了前缀和,看了好几遍看不懂,才知道这种写法不是我这种DP小白能看懂的。然后我找到了另一种做法——母函数做法,一看就看懂了,简单说说我的理解。

首先,从题意入手,两人之间的最后总分分别是多少没有意义,有意义的两人总分差值是多少,从差值的正负可以判断最后的胜利。那么我们需要观察的是a-b(或者b-a),而不是a和b。然后考虑之后的t轮操作,我们发现两个人对差值进行t轮操作,就相当于一个人对差值进行2t轮操作。那么题目就转换成a-b加上2t个[-k,k]之间的整数,最后结果大于0的可能方案数?

我们用母函数构造一轮操作,就是 (xk+xk+1+...+x1+1+x1+x2+...+xk) 负数的话,很难分辨,那么我们每一轮乘以 xk ,得到 1+x+x2+...+x2k1+x2k ,那么2t轮操作就是 (1+x+x2+...+x2k1+x2k)2t ,那么最后 xj 的系数就表示加了 2t 轮之后,a-b加上了 j2kt 的可能方案数。那么显然我们要求的是 0<ab+j2kt 的所有情况的系数和(当然还要满足 j4kt ,即要满足 ba+2kt<j4kt )。

考虑怎么求解系数

(1+x+x2+...+x2k1+x2k)2t=(1x2k+11x)2t=(1x2k+1)2t(11x)2t=(1x2k+1)2t(1+x+x2+x3+...)2t

前一个因式的系数可以使用二项式定理求解,而第二个因式的系数可以使用组合数学意义来求解。设后一个因式可能达到的次数为g,那么 xg 的系数就是求 x1+x2+x3+...+x2t=g xi0 的方程解的个数,按照组合数学的理论,很显然就是 C(gg+2t1)

最后,在写法上可以按 0 2k+1 2(2k+1) 、。。。这样枚举前一个因式的指数,将区间减去这个指数(当区间完全到了负半轴时,枚举结束),然后求解此时区间在第二个因式范围内所有象的系数和,当然不要忘记乘以此时第一个因式的系数。在求第二因式的区间系数和的时候,不需要一个一个求了,可以使用前缀和。

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          401000+10
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

/*inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

//--------------------------------------------------

ll fac[maxn];
ll inv[maxn];
ll dat[maxn];
ll qlow(ll a,ll n){
    ll ans=1;
    while(n){
        if(n&1)ans=(ans*a)%mod;
        a=(a*a)%mod;
        n>>=1;
    }
    return ans;
}
void init(){
    inv[0]=fac[0]=1;
    for(int i=1;i<=401000;i++){
        fac[i]=fac[i-1]*i%mod;
        inv[i]=qlow(fac[i],mod-2);
    }
}
ll C(int n,int m){
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int a,b,k,t;
    init();
    scanf("%d %d %d %d",&a,&b,&k,&t);
    ll L=b-a+2*k*t,R=4*k*t;
    if(L>R){
        puts("0");
        return 0;
    }
    dat[0]=1;
    for(int i=1;i<=4*k*t;i++)
        dat[i]=(dat[i-1]+C(i+2*t-1,i))%mod;
    ll ans=0;
    for(int i=0;1;i++){
        if(i){
            L-=(2*k+1);
            R-=(2*k+1);
        }
        if(R<0)break;
        int op=(i&1)?-1:1;
        if(L<0)ans=(ans+op*C(2*t,i)*dat[R]%mod+mod)%mod;
        else ans=(ans+op*C(2*t,i)*(dat[R]-dat[L])%mod+mod)%mod;
    }
    cout<<ans<<endl;
    return 0;
}


E. Memory and Casinos

分析:这道题也很难,但是主要是难在想法上,写法并不难。

题意:有n个赌场,排成一排,顺次编号。在第i家赌场赢的概率是 pi ,当你赢的时候就会离开第i家赌场,来到第i+1家赌场(或者你在第n家赌场赢了就会离开这个赌场游戏);当你输的时候也会离开第i家赌场,来到i-1家赌场(或者你在第1家赌场输了也会离开这个赌场游戏)。有两种操作:第一种,将第i家赌场赢的概率修改成 ai ;第二种操作,假如我进入第l家赌场赌钱,最后以赢的身份离开第r家赌场的概率是多少?

分析:这里是用线段树,至于为什么,可能是因为多组修改和查询,也可能是数据的不大不小,更多可能是一种灵光一闪。

按照线段树的角度来看,那么叶子结点存的肯定是在这家赌场赢的概率,然后推广到线段,知道分支结点存的是从左边L进入、以赢的身份从R出去的概率,不妨设为 L[l,r] 。那么,很显然,从左边L进入、以输的身份从L出去的概率为1- L[l,r] 。然后考虑怎么将小的区间合并成大的区间。我们假设已知 L[l,m] L[m+1,r] 的所有属性(为了简便,使得 L[l,m]=l1 L[m+1,r]=l2 ),要求 L[l,r] 的所有属性,我们考虑中间从m到m+1通过了几次

l1l2l1(1l2)?l2

我们发现出了问题,?无法用数学语言表示,?表示的是从右边r进入、以赢的身份从r出去的概率。最简单的办法,就是继续设,设它为 R[l,r] 。和上面一样,已知了 R[l,m] R[m+1,r] (同样假设, R[l,m]=r1 , R[m+1,r]=r2 ),求解 R[l,r]

这样先写出前面的概率式

l1l2l1(1l2)r1l2l1((1l2)r1)2l2......

按照等比数列求和法得到总概率是 l1l21(1l2)r1

这样L部分就结束了,还得考虑R部分

同样考虑通过从m+1到m通过的次数

r2(1r2)r1l2(1r2)r1(1l2)r1l2(1r2)r1((1l2)r1)2l2......

按照等比数列求和法得到总概率是 r2+(1r2)r1l21(1l2)r1

之后按照线段区间合并的写法,随便写写就可以了。


By shengtao96, contest: Codeforces Round #370 (Div. 2), problem: (E) Memory and Casinos, Accepted, #
 #include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          100000+10
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

/*inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

//--------------------------------------------------

double L[maxn<<2];
double R[maxn<<2];
void pushup(int rt){
    L[rt]=L[rt<<1]*L[rt<<1|1]/(1-(1-L[rt<<1|1])*R[rt<<1]);
    R[rt]=R[rt<<1|1]+(1-R[rt<<1|1])*R[rt<<1]*L[rt<<1|1]/(1-(1-L[rt<<1|1])*R[rt<<1]);
}
void build(int l,int r,int rt){
    if(l==r){
        int a,b;
        scanf("%d %d",&a,&b);
        L[rt]=R[rt]=1.0*a/b;
        return ;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
void update(int l,int r,int rt,int p,double x){
    if(l==r){
        L[rt]=R[rt]=x;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m)update(lson,p,x);
    else update(rson,p,x);
    pushup(rt);
}
double query(int l,int r,int rt,int LL,int RR,double& y){
    //cout<<l<<" "<<r<<" "<<rt<<endl;
    if(LL<=l&&r<=RR){
        y=R[rt];
        return L[rt];
    }
    int m=(l+r)>>1;
    if(m<LL)return query(rson,LL,RR,y);
    else if(m>=RR)return query(lson,LL,RR,y);
    else {
        double r1,r2,l1,l2;
        l1=query(lson,LL,RR,r1);
        l2=query(rson,LL,RR,r2);
        y=r2+(1-r2)*r1*l2/(1-(1-l2)*r1);
        return l1*l2/(1-(1-l2)*r1);
    }       
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int n,q;
    scanf("%d %d",&n,&q);
    build(1,n,1);
    while(q--){
        int op,a,b,k;
        scanf("%d",&op);
        if(op==1){
            scanf("%d %d %d",&k,&a,&b);
            update(1,n,1,k,1.0*a/b);
        }
        else{
            scanf("%d %d",&a,&b);
            double y;
            printf("%.10f\n",query(1,n,1,a,b,y));
        }
    }
    return 0;
}

这篇题解有点晚,但希望对读者有所帮助吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值