Codeforces Round #631 (Div. 1) / contest 1329


题目地址:https://codeforces.com/contest/1329



A Dreamoon Likes Coloring

题意:有 n 个格子,m 个操作,每一次操作可以在 [ 1 , n − l i + 1 ] [1,n-l_i+1] [1,nli+1] 中选择一个 p i p_i pi,然后把 [ p i , p i + l i − 1 ] [p_i,p_i+l_i-1] [pi,pi+li1] 涂上第 i 中颜色,求一个选择方案,使得最终所有格子都被涂色,并且每种颜色(共m种)都有,或者说明不存在方案。

思路:首先对于每种颜色,优先在更左边的地方涂,因为感性的认知到这样可以给后面更多选择的机会,然后就是根据已有信息判断当前是否可以涂色。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
const int maxn=1e5+5;
LL l[maxn],s[maxn];
int n,m,ans[maxn],last;
 
bool can(int i,int j)
{
    if(j>n-l[i]+1) return 0;
    if(n-j<m-i-1+l[m]) return 0;
    if(n-j-l[i]+1>s[i+1]) return 0;
    if(j-last>l[i-1]) return 0;
    return 1;
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    n=read(),m=read();
    REP(i,1,m) l[i]=read();
    REP_(i,m,1) s[i]=s[i+1]+l[i];
    if(s[1]<n || m-1+l[m]>n) return puts("-1"),0;
    ans[1]=1; last=1;
    int cur=2;
    REP(i,2,m)
    {
        if(i==m) {ans[i]=n-l[i]+1; break;}
        while(cur<=n && !can(i,cur)) cur++;
        if(cur>n) break;
        ans[i]=cur;
        last=cur++;
        //cout<<i<<' '<<ans[i]<<endl;
    }
    if(m>1 && cur>n) return puts("-1"),0;
    REP(i,1,m) printf("%d ",ans[i]);
 
    return 0;
}



B Dreamoon Likes Sequences

题意:给定两个正整数 d 和 m( 1 ≤ d , m ≤ 1 0 9 1\leq d,m\leq 10^9 1d,m109),求正整数数列 a 的种数,使得这个这个数列 a 满足:(1)严格单调递增;(2)最后一项不大于 d;(3)异或前缀和严格单调递增。并输出种数取模 m。

思路:设 h(x) 表示 x 的最高位 1 的位置,那么必然存在 h ( a i ) < h ( a i + 1 ) h(a_i)<h(a_{i+1}) h(ai)<h(ai+1) ,首先根据单调性 ≤ \leq 是很容易求出来的,那为什么不能等于呢?因为如果等于的话,异或前缀和就不满足单调性了(可以用归纳法推出)。于是根据 d 的范围可以得知数列长度不会超过30 。那么我们只用根据 d 的值确定 h(x) 值域的每个值对应的方案数(包括0,也就是不取这个最高位的),再累乘最后减1就是总方案数了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
int h(int x) {REP_(i,30,0) if(x&(1<<i)) return i;}
int f[60],m;
 
int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        int d=read(); m=read();
        int high=h(d); f[0]=1;
        REP(i,1,high-1) f[i]=f[i-1]*2%m;
        int ans=1;
        REP(i,0,high-1) ans=1ll*ans*(f[i]+1)%m;
        ans=1ll*ans*(d-(1<<high)+1+1)%m;
        printf("%d\n",(ans-1+m)%m);
    }
 
    return 0;
}



C Drazil Likes Heap

题意:给出一个数组模拟的完全二叉大根堆(每一层个数都是2的幂),深度为 h,要求对这个堆进行 2 h − 2 g 2^h-2^g 2h2g 次操作,使得最终这个堆为一个深度为 g 的完全二叉大根堆,并且和最小。这个操作为:选择一个不为0的结点,删除,并且把其大儿子路径上每个结点往父结点方向移一位。初始每个结点数据为不相等的正整数。

思路:这题有个需要注意的点就是,要保证最终的堆是一个完全二叉堆。所以每次操作的时候都要判断该操作是否可行(具体来说就是如果删去这个结点,那么该结点大儿子路径的深度是否小于 g )。主体思路其实很简单,就是贪心,把所有元素从大到小排序,然后一个一个判断是否删去,不过实现的时候要把哪个元素在数组哪个位置维护好,就可以了。

还需要注意的是每组数据都要给两倍的空间清零,因为会涉及到叶子的儿子。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
const int maxn=(1<<21)+5;
int w[maxn],a[maxn],T,h,g,b[maxn],n,m,v[maxn];
 
#define chl i<<1
#define chr i<<1|1
int maxh(int i)
{
    if(!a[chl] && !a[chr])
    {
        int ret=0;
        while(i) ret++,i>>=1;
        return ret-1;
    }
    if(a[chl]>a[chr]) return maxh(chl);
    else return maxh(chr);
}
 
void del(int i)
{
    if(!a[chl] && !a[chr]) a[i]=0;
    else if(a[chl]>a[chr]) w[a[chl]]=i, a[i]=a[chl], del(chl);
    else w[a[chr]]=i, a[i]=a[chr], del(chr);
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    T=read();
    while(T--)
    {
        h=read(),g=read(); n=(1<<h)-1; m=(1<<g)-1;
        fill(a,a+n*2+5,0);
        REP(i,1,n) a[i]=read(),w[a[i]]=i,b[i]=a[i];
        sort(b+1,b+n+1,greater<int>());
        int tot=0;
        REP(i,1,n)
        {
            if(tot>=n-m) break;
            int temp=maxh(w[b[i]]);
            if(temp>=g)
            {
                v[tot++]=w[b[i]];
                del(w[b[i]]);
            }
        }
        LL ans=0;
        REP(i,1,m) ans+=a[i];
        printf("%lld\n",ans);
        REP(i,0,tot-1) printf("%d ",v[i]);
        puts("");
    }
 
    return 0;
}



D

题意

思路

代码




E

题意

思路

代码


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值