2021暑期cf加训2

比赛链接:https://codeforces.com/group/2g1PZcsgml/contest/338475

A,B,C,F,H,I,J,K;12;今天貌似比之前好了一点。

A

分析:

最长上升子序列。

代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int f[55][30];
char s[55];
int main()
{
    scanf("%s",&s); int n=strlen(s);
    f[0][s[0]-'a']=1;
    for(int i=1;i<n;i++)
    {
        int c=s[i]-'a'; f[i][c]=1;//
        for(int j=0;j<26;j++)f[i][j]=max(f[i][j],f[i-1][j]);
        for(int j=0;j<c;j++)f[i][c]=max(f[i][c],f[i-1][j]+1);
        //printf("f[%d][%d]=%d\n",i,c,f[i][c]);
    }
    int mx=0;
    for(int k=0;k<26;k++)mx=max(mx,f[n-1][k]);
    printf("%d\n",26-mx);
    return 0;
}
me

 

B

分析:

BFS。

代码如下:

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int const N=55;
int n,m,len,dx[4],dy[4],stx,sty,edx,edy,ans,vis[N][N][N];
char s[N][N],op[N];
struct Nd{
    int x,y,p,cnt;
    bool operator < (const Nd &a) const
    {return cnt>a.cnt;}
};
priority_queue<Nd>q;
void init()
{
    dx[0]=0; dy[0]=-1; dx[1]=1; dy[1]=0; dx[2]=0; dy[2]=1; dx[3]=-1; dy[3]=0;
    for(int i=0;i<=n;i++)
        for(int j=0;j<m;j++)
        {
            if(s[i][j]=='R')stx=i,sty=j;
            if(s[i][j]=='E')edx=i,edy=j;
        }
}
bool can(int x,int y){if(x<0||x>=n||y<0||y>=m)return 0; return s[x][y]!='#';}//=='.'走不到'E'!
int chg(char c){if(c=='L')return 0; if(c=='D')return 1; if(c=='R')return 2; if(c=='U')return 3;}
void bfs()
{
    memset(vis,0x3f3f3f3f,sizeof vis);
    q.push((Nd){stx,sty,0,0});
    int nx,ny;
    while(q.size())
    {
        Nd t=q.top(); q.pop();
        if(t.x==edx&&t.y==edy){ans=t.cnt; break;}
        if(vis[t.x][t.y][t.p]<=t.cnt)continue;
        vis[t.x][t.y][t.p]=t.cnt;
        //printf("x=%d y=%d p=%d cnt=%d vis=%d\n",t.x,t.y,t.p,t.cnt,vis[t.x][t.y][t.p]);
        //bool fl=0;
        //if(t.x==0&&t.y==1&&t.p==2)fl=1,printf("!cnt=%d\n",t.cnt);
        for(int i=0;i<=3;i++){
            if(can((nx=t.x+dx[i]),(ny=t.y+dy[i]))&&vis[nx][ny][t.p]>t.cnt+1)
                q.push((Nd){nx,ny,t.p,t.cnt+1});
            //if(fl)printf("can(%d,%d)=%d vis=%d t.cnt+1=%d\n",nx,ny,can(nx,ny),vis[nx,ny,t.p],t.cnt+1);
        }
        //fl=0;
        if(t.p>=len)continue;
        q.push((Nd){t.x,t.y,t.p+1,t.cnt+1});
        int dr=chg(op[t.p]);
        //if(t.x==1&&t.y==2&&t.p==3)fl=1,printf("!!cnt=%d\n",t.cnt);
        if(can((nx=t.x+dx[dr]),(ny=t.y+dy[dr]))){
                q.push((Nd){nx,ny,t.p+1,t.cnt});
                //if(fl)printf("nx=%d ny=%d\n",nx,ny);
        }
        else q.push((Nd){t.x,t.y,t.p+1,t.cnt});
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)scanf("%s",&s[i]);
    scanf("%s",&op); len=strlen(op);
    init(); bfs();
    printf("%d\n",ans);
    return 0;
}
me

 

C

分析:

过一遍即可。

代码如下:

#include<iostream>
using namespace std;
int const N=1e5+5;
int n,k,len,a[N],ans;
int main()
{
    scanf("%d%d%d",&n,&k,&len);
    for(int i=1,p;i<=k;i++)
        scanf("%d",&p),a[p]=1;
    int num=0,l=1,r=len;
    for(int i=1;i<=len;i++)num+=a[i];
    for(;r<=n;r++)
    {
        if(num==0)a[r]=1,a[r-1]=1,ans+=2,num=2;
        else if(num==1)
        {
            if(a[r])a[r-1]=1; else a[r]=1;
            ans++; num=2;
        }
        num-=a[l]; num+=a[r+1];
        l++;
    }
    printf("%d\n",ans);
    return 0;
}
me

 

H

分析:

直接按右端点排序,DP。

注意离散化不要写错了!

代码如下:

#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int const N=4e5+5;
int n,cnt;
ll R,tmp[N],f[N];
struct Nd{
    ll l,r;
}a[N];
bool cmp(Nd a,Nd b){return a.r<b.r;}
int main()
{
    scanf("%lld%d",&R,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i].l,&a[i].r);
        tmp[++cnt]=a[i].l; tmp[++cnt]=a[i].r;
    }
    sort(a+1,a+n+1,cmp);
    sort(tmp+1,tmp+cnt+1); cnt=unique(tmp+1,tmp+cnt+1)-tmp-1;///-1!!!
    for(int i=1;i<=n;i++)
        a[i].l=lower_bound(tmp+1,tmp+cnt+1,a[i].l)-tmp,
        a[i].r=lower_bound(tmp+1,tmp+cnt+1,a[i].r)-tmp;
    int p=1;
    for(int i=1;i<=cnt;i++)
    {
        f[i]=f[i-1];
        while(p<=n&&a[p].r<=i)f[i]=max(f[i],f[a[p].l-1]-tmp[a[p].l]+tmp[a[p].r]+1),p++;
        //printf("i=%d tmp=%lld f=%lld\n",i,tmp[i],f[i]);
    }
    printf("%lld\n",R-f[cnt]);
    return 0;
}
me

 

I

分析:

贪心先放远处。

代码如下:

#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int const N=1005;
int n,k,ca,cb;
ll ans;
struct Nd{
    int x,m;
}a[N],b[N];
int ab(int x){return (x>0)?x:-x;}
bool cmp(Nd aa,Nd bb){return ab(aa.x)<ab(bb.x);}
void work1()
{
    for(int i=ca;i;i--)
    {
        if(a[i].m==0)continue;
        if(a[i].m>=k)ans+=(ll)a[i].x*(a[i].m/k)*2,a[i].m%=k;
        if(a[i].m==0)continue;
        ans+=(ll)a[i].x*2;
        int res=k-a[i].m,p=i-1; a[i].m=0;
        while(p&&res)
        {
            if(res>a[p].m)res-=a[p].m,a[p].m=0,p--;
            else a[p].m-=res,res=0;
        }
    }
}
void work2()
{
    for(int i=cb;i;i--)
    {
        if(b[i].m==0)continue;
        if(b[i].m>=k)ans+=(ll)(-b[i].x)*(b[i].m/k)*2,b[i].m%=k;
        if(b[i].m==0)continue;
        ans+=(ll)(-b[i].x)*2;
        //printf("a[%d].x=%d m=%d ans=%lld\n",i,a[i].x,a[i].m,ans);
        int res=k-b[i].m,p=i-1; b[i].m=0;
        while(p&&res)
        {
            if(res>b[p].m)res-=b[p].m,b[p].m=0,p--;
            else b[p].m-=res,res=0;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1,xx,mm;i<=n;i++)
    {
        scanf("%d%d",&xx,&mm);
        if(xx>=0)a[++ca].x=xx,a[ca].m=mm;
        else b[++cb].x=xx,b[cb].m=mm;
    }
    sort(a+1,a+ca+1,cmp); sort(b+1,b+cb+1,cmp);
    work1(); work2();
    printf("%lld\n",ans);
    return 0;
}
/*
4 10
-7 5
-2 3
5 7
9 5
*//*
7 1
9400000 10000000
9500000 10000000
9600000 10000000
9700000 10000000
9800000 10000000
9900000 10000000
10000000 10000000
*/
me

 

J

分析:

一个数\( v \)最多会被\( mod log(v) \)次,所以问题是怎么快速找到下一个比它小的数;

可以用线段树或ST表做,都是\( log(n) \)的。

代码如下:

#include<iostream>
#define ll long long
#define mid ((l+r)>>1)
#define ls (u<<1)
#define rs ((u<<1)|1)
using namespace std;
int const N=2e5+5;
int n,q,tr[N<<2];
ll a[N],v;
void upt(int u)
{
    if(a[tr[ls]]<=a[tr[rs]])tr[u]=tr[ls];
    else tr[u]=tr[rs];
}
void build(int u,int l,int r)
{
    if(l==r){tr[u]=l; return;}
    build(ls,l,mid); build(rs,mid+1,r);
    upt(u);
}
ll qry(int u,int l,int r,int ql,int qr)
{
    ll ret=0;
    if(l==r)return a[l]<=v?l:0;
    if(l>=ql&&r<=qr)
    {
        if(a[tr[ls]]<=v)return qry(ls,l,mid,ql,qr);
        else if(a[tr[rs]]<=v)return qry(rs,mid+1,r,ql,qr);
        else return 0;
    }
    if(mid>=ql)ret=qry(ls,l,mid,ql,qr);
    if(ret)return ret;
    if(mid<qr)ret=qry(rs,mid+1,r,ql,qr);
    return ret;
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    build(1,1,n);
    for(int i=1,ql,qr;i<=q;i++)
    {
        scanf("%lld%d%d",&v,&ql,&qr);
        while(ql<=qr)
        {
            int ps=qry(1,1,n,ql,qr); if(!ps)break;
            v%=a[ps]; ql=ps+1;
            //printf("ps=%d\n",ps);
        }
        printf("%lld\n",v);
    }
    return 0;
}
me

 

K

分析:

\( f[i] \) 表示至少赢 \( i \) 次的方案数;

\( f[i] = C_{2^k-r}^{2^i-1} / C_{2^k-1}^{2^i-1} \);

直接\( k*2^r \)做会TLE,但是复杂度应该是\( 2*10^7 \)才对,不知为什么;

所以需要利用上一次的答案,每次转移\( O(2^{i-1}) \),就能过了。

代码如下:

#include<iostream>
#include<cmath>
#define db double
using namespace std;
int const N=(1<<20)+5;
db const eps=1e-6;
int k,r;
db f[25],ans;
int main()
{
    scanf("%d%d",&k,&r); f[0]=1;
    for(int i=1;i<=k;i++)
    {
        if((1<<i)-1>(1<<k)-r)break;
        //f[i]=1; int L=(1<<k)-(1<<i)-r+2,L2=(1<<k)-r+1,R=(1<<k)-(1<<i),R2=(1<<k)-1;
        //for(int j=L,j2=L2;j<=R;j++,j2++)f[i]*=j,f[i]/=j2;
        f[i]=f[i-1]; int A=(1<<k)-r-(1<<i)+1,B=(1<<k)-(1<<i);
        for(int j=1;j<=(1<<(i-1));j++)f[i]*=(A+j),f[i]/=(B+j);
    }
    for(int i=1;i<=k;i++)ans+=(f[i]-f[i+1])*i;
    printf("%.5lf\n",ans);
    return 0;
}
me

 

posted @ 2021-07-30 21:14  Zinn  阅读(57)  评论(0编辑  收藏  举报