题目地址:https://codeforces.com/contest/1329
A Dreamoon Likes Coloring
题意:有 n 个格子,m 个操作,每一次操作可以在 [ 1 , n − l i + 1 ] [1,n-l_i+1] [1,n−li+1] 中选择一个 p i p_i pi,然后把 [ p i , p i + l i − 1 ] [p_i,p_i+l_i-1] [pi,pi+li−1] 涂上第 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 1≤d,m≤109),求正整数数列 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 2h−2g 次操作,使得最终这个堆为一个深度为 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
题意:
思路:
代码: