cf 1557(div2)

比赛链接:https://codeforces.com/contest/1557

一小时做完了A,B,C,剩下一小时想D,也没想出来。不过最终是500多名,上了一百多分,真舒适。因为是和排名有关的计分,所以做题速度很重要啊。

 

A

分析:

当时感觉是直接让最大数一组,剩下的另一组,写了就过了;

详细证明 Tutorial 写了。

代码如下:

#include<iostream>
#include<algorithm>
#define db double
using namespace std;
int const N=1e5+5;
int T,n;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n); db sum=0; int mx=-1e9-1;
        for(int i=1,x;i<=n;i++)
        {
            scanf("%d",&x); sum+=x;
            if(x>mx)mx=x;
        }
        printf("%lf\n",mx+(sum-mx)/(n-1));
    }
    return 0;
}
A

 

B

分析:

离散化一下,找最多的连续上升子段的个数,小于等于\( k \)就可以,因为它们内部可以随便再分。

代码如下:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int const N=1e5+5;
int T,n,k,a[N],b[N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+n+1);
        for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
        int cnt=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i]==a[i-1]+1)continue;
            else cnt++;
        }
        if(cnt<=k)puts("Yes");
        else puts("No");
    }
    return 0;
}
B

 

C

分析:

如果\( n \)是奇数,那么\( \& \)为\( 1 \)的位上\( \bigoplus \)也是\( 1 \);所以要枚举\( k \)个位置上一共有几个\( 1 \),分别在哪儿,然后让其他位上都是\( 0 \)。

如果\( n \)是偶数,那么\( \& \)为\( 1 \)的位上\( \bigoplus \)是\( 0 \);所以枚举哪个位置出现第一个\( 1 \),让前面的位都是\( 0 \),后面的位随便取;还要加上没有\( 1 \)的方案数。

让一个位保证是\( 0 \),也就是找到\( < n \)的偶数个数,让它们在该位取\( 1 \),其他数在该位取\( 0 \)。

懒得写公式了所以用文字描述了半天;公式看代码即知。

代码如下:

#include<iostream>
#define ll long long
using namespace std;
int const N=2e5+5,md=1e9+7;
int T,n,k;
ll cc,ans,fc[N],inv[N];
ll pw(ll x,ll y)
{
    ll ret=1,a=x%md;
    while(y)
    {
        if(y&1)ret=ret*a%md;
        a=a*a%md;
        y>>=1;
    }
    return ret;
}
ll C(int a,int b){return a<b?0:((fc[a]*inv[b])%md*inv[a-b])%md;}
void init()
{
    cc=0; ans=0;
    for(int i=0;i<n;i+=2)cc=(cc+C(n,i))%md;
}
int main()
{
    scanf("%d",&T);
    fc[0]=1;
    for(int i=1;i<N;i++)fc[i]=(fc[i-1]*i)%md;
    inv[N-1]=pw(fc[N-1],md-2);
    for(int i=N-2;i>=0;i--)inv[i]=(inv[i+1]*(i+1))%md;
    while(T--)
    {
        scanf("%d%d",&n,&k); init();
        if(k==0){printf("1\n"); continue;}
        if(n%2)
        {
            for(int i=0;i<=k;i++)
                ans=(ans+C(k,i)*pw(cc,k-i)%md)%md;
        }
        else
        {
            ans=pw(cc,k);
            for(int i=1;i<=k;i++)
                ans=(ans+pw(cc,k-i)*pw(pw(2,n),i-1)%md)%md;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
C

 

D

分析:

比赛时想了想线段树、DP,但没有成形的想法。后来做别的去了。

这题确实是DP,关键是每一行要找到一行来更新它。开一个线段树,每个点存了一个\(val\)和一个\(id\),表示在这一点为\(1\),最大能保留多少行,最后一行是第几行。对于第\(i\)行,找线段树上它是\(1\)的所有区间中\(val\)的最大值,然后把它继承下来,线段树上它所有区间更新成\(val=max\left \{ val,mx.val+1 \right \}\);若成功更新,对应区间的\(id=i\)。

过程中记录\(pre[i]\),表示更新第\(i\)行的是哪一行。再记录最大的\(val\)以及它出现在哪一行。最后就可以找到最大\(val\)对应的路径,然后输出答案了。

代码如下:

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#define mp make_pair
#define fst first
#define scd second
#define pb push_back
#define ls (u<<1)
#define rs ((u<<1)|1)
#define mid ((l+r)>>1)
using namespace std;
int const N=6e5+5;
int n,m,c[N],cnt,pre[N],vis[N];
struct Nd{
    int val,id;
}tr[N<<2],lz[N<<2];
vector<pair<int,int> >qu[N];
void build(int u,int l,int r)
{
    if(l==r){tr[u].val=0; tr[u].id=-1; return;}
    build(ls,l,mid); build(rs,mid+1,r);
}
Nd bet(Nd a,Nd b){return (a.val>b.val)?a:b;}
void pup(int u){tr[u]=bet(tr[ls],tr[rs]);}
void pdn(int u)
{
    if(lz[u].val==0)return;
    tr[ls]=lz[u]; lz[ls]=lz[u];
    tr[rs]=lz[u]; lz[rs]=lz[u];
    lz[u]=(Nd){0,-1};
}
Nd find(int u,int l,int r,int ql,int qr)
{
    //printf("fd(%d,%d,%d,%d,%d)\n",u,l,r,ql,qr);
    if(l>r)return (Nd){0,-1};
    if(l>=ql&&r<=qr)return tr[u];
    pdn(u); Nd ret=(Nd){0,-1};
    if(mid>=ql)ret=bet(ret,find(ls,l,mid,ql,qr));
    if(mid<qr)ret=bet(ret,find(rs,mid+1,r,ql,qr));
    return ret;
}
void upt(int u,int l,int r,int ql,int qr,Nd s)
{
    if(l>r)return;
    if(l>=ql&&r<=qr)
    {
        if(tr[u].val<=s.val)tr[u]=s,lz[u]=s;
        else if(l==r)return;
        else
        {
            pdn(u);
            if(tr[ls].val<=s.val)tr[ls]=s,lz[ls]=s;
            else upt(ls,l,mid,ql,qr,s);
            if(tr[rs].val<=s.val)tr[rs]=s,lz[rs]=s;
            else upt(rs,mid+1,r,ql,qr,s);
            pup(u);
        }
        return;
    }
    pdn(u);
    if(mid>=ql)upt(ls,l,mid,ql,qr,s);
    if(mid<qr)upt(rs,mid+1,r,ql,qr,s);
    pup(u);
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,t,l,r;i<=m;i++)
    {
        scanf("%d%d%d",&t,&l,&r);
        qu[t].pb(mp(l,r));
        c[++cnt]=l; c[++cnt]=r;
    }
    sort(c+1,c+cnt+1); cnt=unique(c+1,c+cnt+1)-c-1;
    build(1,1,cnt);
    int ans=0,row=-1;
    memset(pre,-1,sizeof pre);
    for(int i=1;i<=n;i++)
    {
        Nd mx=(Nd){0,-1};
        for(pair<int,int> it:qu[i])
        {
            int ql=lower_bound(c+1,c+cnt+1,it.fst)-c,qr=lower_bound(c+1,c+cnt+1,it.scd)-c;
            mx=bet(mx,find(1,1,cnt,ql,qr));
        }
        //printf("i=%d mx.val=%d mx.id=%d\n",i,mx.val,mx.id);
        pre[i]=mx.id;
        Nd nw=(Nd){mx.val+1,i};
        if(nw.val>ans)ans=nw.val,row=i;
        for(pair<int,int> it:qu[i])
        {
            int ql=lower_bound(c+1,c+cnt+1,it.fst)-c,qr=lower_bound(c+1,c+cnt+1,it.scd)-c;
            upt(1,1,cnt,ql,qr,nw);
        }
    }
    printf("%d\n",n-ans);
    while(row>-1)vis[row]=1,row=pre[row];
    for(int i=1;i<=n;i++)
        if(!vis[i])printf("%d ",i);
    return 0;
}
D

 

posted @ 2021-08-10 11:17  Zinn  阅读(35)  评论(0编辑  收藏  举报