【NOIP2017练习】 怎样更有力气
题面描述
长者的住宅中有一堵长度为n的墙。每天抖儿起床修行,会选择一段长度为x的区间染成白色。长者的住宅附近有一群记者,为了借助抖儿拜访长者,第
输出
点击查看代码
#include<bits/stdc+.h>
using namespace std;
signed main(){
cout<<"Poor Douer!"<<endl;
}
n才10?
考虑状态压缩dp!
0,1表示放还是不放即可(WA是溢出了)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int ans=1e9,n,m,X,ll[100100],rr[100100],mapp;
signed main(){
cin>>n>>m>>X;
for(int i=1;i<=m;i++){
int tt=0;
cin>>ll[i]>>rr[i];
for(int j=ll[i];j<=rr[i];j++){
mapp|=(1<<(j-1));
}
for(int j=0;j<(1<<n)-1;j++){
int t=mapp;
for(int k=0;k<X;k++){
t|=((j<<k));
}
t=t%(1<<n);
if(t!=(1<<n)-1){
continue;
}
int tt=0;
for(int k=j;k;k-=(k&(-k))){
tt++;
}
if(tt<=i){
cout<<i;
return 0;
}
}
}
cout<<"Poor Douer!"<<endl;
}
很容易发现
等等, +区间修改???
线段树!!!(超开心的大喊)(扭曲爬行)(变成点烤烤)
注:
是为后面的 做铺垫
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,ll[1000100],rr[1000100];
struct zxcdasshabiyinggaichedidebaoling_zhehuiwojueduibudiandigantanhao{
//不要试图看懂↑
int l,r,sum,lazy;
}tree[5000100];
#define ls (id<<1)
#define rs (id<<1|1)
#define mid ((tree[id].l+tree[id].r)>>1)
#define up tree[id].sum=tree[ls].sum+tree[rs].sum
//个人习惯(define狂魔)
//下面的线段树挺板的,不多说了
void down(int id){
if(tree[id].lazy){
tree[ls].sum=(tree[ls].r-tree[ls].l+1);
tree[rs].sum=(tree[rs].r-tree[rs].l+1);
tree[ls].lazy=1;
tree[rs].lazy=1;
tree[id].lazy=0;
}
}
void build(int l,int r,int id){
tree[id].l=l;
tree[id].r=r;
tree[id].lazy=0;
tree[id].sum=0;
if(l==r){
tree[id].sum=0;
return;
}
build(l,mid,ls);
build(mid+1,r,rs);
up;
}
void add(int l,int r,int id){
if(tree[id].l>=l&&tree[id].r<=r){
tree[id].sum=(tree[id].r-tree[id].l+1);
tree[id].lazy=1;
return;
}
down(id);
if(l<=mid){
add(l,r,ls);
}
if(r>mid){
add(l,r,rs);
}
up;
}
int sum2(int l,int r,int id){
if(tree[id].l>=l&&tree[id].r<=r){
return tree[id].sum;
}
down(id);
int ans=0;
if(x<=mid){
ans=sum2(l,r,ls);
}
else{
ans+=sum2(l,r,rs);
}
return ans;
}
signed main(){
cin>>n>>m>>x;
for(int i=1;i<=m;i++){
cin>>ll[i]>>rr[i];
}
build(1,n,1);
for(int i=1;i<=m;i++){
add(ll[i],rr[i],1);
if(sum2(1,n,1)==n){
cout<<i;
return 0;
}
}
cout<<"Poor Douer!"<<endl;
}
发现可以只记录涂色的起点,那么我们不妨从左到右遍历,如果未涂色,则必须从这里为起点涂色
正确性显然因为前面没涂到,后面影响不到,不从你开始从谁开始?
写个循环跑就行了不是为啥分这么高
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int ans=1e9,n,m,X,ll[100100],rr[100100],mapp[100100];
signed main(){
cin>>n>>m>>X;
for(int i=1;i<=m;i++){
int tt=0;
cin>>ll[i]>>rr[i];
for(int j=ll[i];j<=rr[i];j++){
mapp[j]=1;
}
if(!X){
for(int j=1;j<=n;j++){
if(!mapp[j]){
tt=1e9;
}
}
}
else{
for(int j=1;j<=n;j++){
if(!mapp[j]){
tt++;
j=j+X-1;//直接跳过省去修改
}
}
}
if(tt<=i){
cout<<i;
return 0;
}
}
cout<<"Poor Douer!"<<endl;
}
经过分析,发现记者绝对不会帮倒忙,就是说从某一天之后,以后都能涂完。
难道说?
二分!!!
很好,按照
二分。 那么
函数呢? 可以使用之前的贪心
问题来了
怎么
维护记者涂成什么样子呢???
记者会区间修改。。。
🤨总不能是?
🤩没错!!
😑别!!想想
行代码 😆使用线段树!!!
😨NO!!!
时间辅助度:
实际上卡一卡能
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,ll[100100],rr[100100];
struct zxcdasshabiyinggaichedidebaoling_zhehuiwojueduibudiandigantanhao{
int l,r,sum,lazy;
}tree[500100];
#define ls (id<<1)
#define rs (id<<1|1)
#define mid ((tree[id].l+tree[id].r)>>1)
#define up tree[id].sum=tree[ls].sum+tree[rs].sum
void down(int id){
if(tree[id].lazy){
tree[ls].sum=(tree[ls].r-tree[ls].l+1);
tree[rs].sum=(tree[rs].r-tree[rs].l+1);
tree[ls].lazy=1;
tree[rs].lazy=1;
tree[id].lazy=0;
}
}
void build(int l,int r,int id){
tree[id].l=l;
tree[id].r=r;
tree[id].lazy=0;
tree[id].sum=0;
if(l==r){
tree[id].sum=0;
return;
}
build(l,mid,ls);
build(mid+1,r,rs);
up;
}
void add(int l,int r,int id){
if(tree[id].l>=l&&tree[id].r<=r){
tree[id].sum=(tree[id].r-tree[id].l+1);
tree[id].lazy=1;
return;
}
down(id);
if(l<=mid){
add(l,r,ls);
}
if(r>mid){
add(l,r,rs);
}
up;
}
int sum(int x,int id){
if(tree[id].l==tree[id].r){
return tree[id].sum;
}
down(id);
if(x<=mid){
return sum(x,ls);
}
else{
return sum(x,rs);
}
}
int sum2(int l,int r,int id){
if(tree[id].l>=l&&tree[id].r<=r){
return tree[id].sum;
}
down(id);
int ans=0;
if(x<=mid){
ans=sum2(l,r,ls);
}
else{
ans+=sum2(l,r,rs);
}
return ans;
}
bool check(int mi){
build(1,n,1);
for(int i=1;i<=mi;i++){
add(ll[i],rr[i],1);
}
if(x==0){//不特判会卡死
return sum2(1,n,1)==n;
}
int cnt=0;
for(int i=1;i<=n;i++){
if(!sum(i,1)){
i=i+x-1;//直接跳过省去add(相当于小优化吧)(-1是因为i下一个循环还要+1)(x=0是会因为这里卡死)
cnt++;
}
}
return cnt<=mi;
}
signed main(){
cin>>n>>m>>x;
for(int i=1;i<=m;i++){
cin>>ll[i]>>rr[i];
}
int l=1,r=m;
while(l<=r){
int mi=(l+r)/2;
if(check(mi)){
r=mi-1;
}
else{
l=mi+1;
}
}
if(l>m){
cout<<"Poor Douer!"<<endl;
return 0;
}
cout<<l<<endl;
}
考虑优化
发现朵儿涂一段区间要的天数实际上可以直接用
然后就想到可以将记者染的区间对左端点进行一个排的序,然后记录目前涂到的最右边的端点,这样就可以直接计算朵儿涂墙要的天数力!
结果正解代码比
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x;
struct zxc{
int l,r;
}a[100100],b[100100];
bool cmp(zxc x,zxc y){
return x.l<y.l;
}
bool check(int mi){
for(int i=1;i<=m;i++){
b[i]=a[i];
}
sort(b+1,b+1+mi,cmp);
int R=0;
if(x==0){
for(int i=1;i<=mi;i++){
if(b[i].l>R+1){
return 0;
}
R=max(R,b[i].r);
}
if(R!=n){
return 0;
}
return 1;
}
int sum=0,ans=0;
for(int i=1;i<=mi;i++){
if(b[i].l>R+1){
sum=(b[i].l-R-1)/x;
if((b[i].l-R-1)%x!=0){
sum++;
}
}
else{
sum=0;
}
R=max(b[i].r,R+sum*x);
//这里R要加上sum*x是因为如果朵儿多染了的话,就会比b[i].r大了
ans+=sum;
}
if(n>R){
sum=(n-R)/x;
if((n-R)%x!=0){
sum++;
}
}
else{
sum=0;
}
ans+=sum;
return ans<=mi;
}
signed main(){
cin>>n>>m>>x;
for(int i=1;i<=m;i++){
cin>>a[i].l>>a[i].r;
}
int l=0,r=m;
while(l<=r){
int mi=(l+r)/2;
if(check(mi)){
r=mi-1;
}
else{
l=mi+1;
}
}
if(l>m){
cout<<"Poor Douer!"<<endl;
return 0;
}
cout<<l<<endl;
}
原理
可以看到楼下是一个我甚至嚣张的让他hack我
原理是有人(
if(b[mid].r!=n){
return false;
}
问题在于
画个图:
可以发现,此时排序后1号在前,那么
本文作者:LEWISAK
本文链接:https://www.cnblogs.com/lewisak/p/18270584
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步