很像NOI2014的起床困难综合征
首先每一位拆开来做
从高位到低位贪心,看每一位能不能为0
N<=100的时候可以搞一个判定性DP
N<=2000的时候由于A=1,考虑最优化问题,最小化组数,看是不是小于等于b
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=2000+5;
typedef long long ll;
ll s[N],bin[60];
int n,a,b;
ll change(ll x,int t){
return (x>>t)<<t;
}
bool sub(ll s,ll ans){
return (s|ans)==ans;
}
namespace solve1{
bool f[105][105],g[105][105];
int main(){
rep(i,1,n)
rep(j,i,n)f[i][j]=1;
ll ans=0;
per(t,40,0){
memcpy(g,f,sizeof(f));
rep(i,1,n)f[1][i]&=sub(change(s[i],t),ans);
rep(i,2,n)
rep(j,i,n){
bool flag=false;
rep(k,i-1,j-1){
flag|=f[i-1][k]&&sub(change(s[j]-s[k],t),ans);
}
f[i][j]&=flag;
}
bool flag=false;
rep(i,a,b)flag|=f[i][n];
if(!flag)memcpy(f,g,sizeof(f)),ans|=bin[t];
//rep(i,1,n)
//rep(j,1,n)
//printf("%d %d %d %d\n",t,i,j,f[i][j]);
}
printf("%lld\n",ans);
return 0;
}
}
namespace solve2{
ll ans;
int f[N];
int main(){
per(t,40,0){
mmt(f,0x3f);
f[0]=0;
rep(i,1,n)
rep(j,0,i-1)
if(sub(change(s[i]-s[j],t),ans))
f[i]=min(f[i],f[j]+1);
if(f[n]>b)ans|=bin[t];
}
printf("%lld\n",ans);
return 0;
}
}
int main(){
//freopen("a.in","r",stdin);
bin[0]=1;rep(i,1,50)bin[i]=bin[i-1]<<1;
scanf("%d%d%d",&n,&a,&b);
rep(i,1,n)scanf("%lld",&s[i]),s[i]+=s[i-1];
if(n<=100)return solve1::main();
else return solve2::main();
return 0;
}
暴力跑Dijkstra+一个小优化大概有57分
考虑分块,由于p大于sqrt(N)的点,连出去的边最多sqrt(N)条,暴力加边
而剩下的点,假设p1,p2,p3为三栋建筑,p1到p2距离等于p2到p3距离,假设p1到p2有边且p2到p3有边,那么p1到p3的那条边就不用连了,于是每一种类型的边最多N条,类型最多sqrt(N)种,边数Nsqrt(N)
然后跑最短路就好了
不知道为啥被卡了3分RE(疑似线段树写挂了)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=30000+5;
int d[N<<1];
int tr[N<<2],M;
bool done[N];
bool cmp(int i,int j){
return d[i]<d[j];
}
void update(int n,int v){
if(d[n]<=v)return;
d[n]=v;
for(n+=M,n>>=1;n;n>>=1)
tr[n]=min(tr[n<<1],tr[n<<1|1],cmp);
}
void modify(int n,int v){
d[n]=v;
for(n+=M,n>>=1;n;n>>=1)
tr[n]=min(tr[n<<1],tr[n<<1|1],cmp);
}
void build(){
per(i,M-1,1)tr[i]=min(tr[i<<1],tr[i<<1|1],cmp);
}
struct Edge{int to,next,v;}e[N*200];
int head[N],cnt;
void ins(int u,int v,int w){e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;}
int dijkstra(int s,int t){
mmt(d,0x3f);d[s]=0;build();
while(d[tr[1]]<inf){
int u=tr[1];
if(u==t)return d[u];
done[u]=true;
tra(i,u){
int v=e[i].to;if(done[v])continue;
update(v,d[u]+e[i].v);
}
modify(u,inf);
}
return -1;
}
struct doge{
int pos,step;
bool operator < (const doge &x)const{
return pos==x.pos?step<x.step:pos<x.pos;
}
}dog[N];
bool have[N],step[105][N];
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
M=1;
while(M-2<n)M<<=1;
rep(i,1,n)tr[i+M]=i;
rep(i,1,m){
scanf("%d%d",&dog[i].pos,&dog[i].step);dog[i].pos++;have[dog[i].pos]=true;
if(dog[i].step<=100)step[dog[i].step][dog[i].pos]=true;
}
int s=dog[1].pos,t=dog[2].pos;
sort(dog+1,dog+1+m);
int top=1;
rep(i,2,m)if(dog[i].pos!=dog[top].pos||dog[i].step!=dog[top].step)dog[++top]=dog[i];
m=top;
rep(i,1,m){
int p=dog[i].pos,l=dog[i].step;
if(l>100){
for(int j=1,now=p+l;now<=n;now+=l,j++)if(have[now])ins(p,now,j);
for(int j=1,now=p-l;now>0;now-=l,j++)if(have[now])ins(p,now,j);
}else{
for(int j=1,now=p+l;now<=n;now+=l,j++)if(have[now]){ins(p,now,j);if(step[l][now])break;}
for(int j=1,now=p-l;now>0;now-=l,j++)if(have[now]){ins(p,now,j);if(step[l][now])break;}
}
}
printf("%d\n",dijkstra(s,t));
return 0;
}
显然对于K=1的情况直接找坐标中位数即可
对于K=2的情况我们可以考虑分为两个K=1的情况
以每一对节点的中点为序进行排序,然后枚举分割点,分割点之前的都走桥1,分割点之后的都走桥2
利用权值线段树/平衡树/树状数组/堆维护中位数信息,O(logn)找到桥1桥2的位置以及点到他们的距离和。
取最小值即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=100000+5;
typedef long long ll;
ll iabs(ll x){return x>0?x:-x;}
ll x[N],y[N],base;
int n,k;
namespace solve1{
ll pos[N<<1];
void main(){
rep(i,1,n)pos[i<<1]=x[i],pos[2*i-1]=y[i];
sort(pos+1,pos+1+2*n);
ll ans=0,p=pos[n];
rep(i,1,2*n)
ans+=iabs(p-pos[i]);
printf("%lld\n",ans+base+n);
}
}
namespace solve2{
struct point{
ll x,y,m;
bool operator < (const point &p)const{
return m<p.m;
}
}p[1005];
ll pos[2005];
ll work(int m){
if(m<=1)return 0;
sort(pos+1,pos+1+m);
ll p=pos[(m+1)>>1];
ll ans=0;
rep(i,1,m)ans+=iabs(p-pos[i]);
return ans;
}
void main(){
rep(i,1,n)p[i]=(point){x[i],y[i],x[i]+y[i]>>1};
sort(p+1,p+1+n);
ll ans=1LL<<60;
rep(i,0,n){
ll tmp=0;
int top=0;
rep(j,1,i)pos[++top]=p[j].x,pos[++top]=p[j].y;
tmp+=work(top);
top=0;
rep(j,i+1,n)pos[++top]=p[j].x,pos[++top]=p[j].y;
tmp+=work(top);
ans=min(ans,tmp);
}
if(!n)ans=0;
printf("%lld\n",ans+base+n);
}
}
namespace solve3{
struct point{
ll x,y,m;
bool operator < (const point &p)const{
return m<p.m;
}
}p[N];
ll hash[N<<1];
struct Node{
int l,r,sz;
ll sum;
};
struct segment_tree{
Node tr[N<<3];
#define lc o<<1
#define rc o<<1|1
void build(int o,int l,int r){
tr[o].l=l;tr[o].r=r;
if(l==r)return;
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
}
void pushup(int o){tr[o].sz=tr[lc].sz+tr[rc].sz;tr[o].sum=tr[lc].sum+tr[rc].sum;}
void insert(int o,int p){
int l=tr[o].l,r=tr[o].r;
if(l==r)tr[o].sz++,tr[o].sum+=hash[p];
else{
int mid=l+r>>1;
if(p<=mid)insert(lc,p);
else insert(rc,p);
pushup(o);
}
}
void del(int o,int p){
int l=tr[o].l,r=tr[o].r;
if(l==r)tr[o].sz--,tr[o].sum-=hash[p];
else{
int mid=l+r>>1;
if(p<=mid)del(lc,p);
else del(rc,p);
pushup(o);
}
}
int find(int o,int k){
int l=tr[o].l,r=tr[o].r;
if(l==r)return l;
else if(k<=tr[lc].sz)return find(lc,k);
else return find(rc,k-tr[lc].sz);
}
ll sum(int o,int k){
int l=tr[o].l,r=tr[o].r;
if(l==r)return hash[l]*k;
else if(k<=tr[lc].sz)return sum(lc,k);
else return tr[lc].sum+sum(rc,k-tr[lc].sz);
}
ll calc(){
if(!tr[1].sz)return 0;
int k=tr[1].sz+1>>1,p=find(1,k);
return hash[p]*k-2*sum(1,k)+tr[1].sum-hash[p]*(tr[1].sz-k);
}
}t1,t2;
int m;
int find(ll x){
return lower_bound(hash+1,hash+1+m,x)-hash;
}
void main(){
rep(i,1,n)p[i]=(point){x[i],y[i],x[i]+y[i]>>1},hash[2*i-1]=x[i],hash[2*i]=y[i];
sort(p+1,p+1+n);sort(hash+1,hash+1+2*n);
m=unique(hash+1,hash+1+2*n)-hash-1;
t1.build(1,1,m);t2.build(1,1,m);
ll ans=n?(1LL<<60):0;
rep(i,1,n)p[i].x=find(p[i].x),p[i].y=find(p[i].y);
rep(i,1,n)t2.insert(1,p[i].x),t2.insert(1,p[i].y);
ans=min(ans,t2.calc());
rep(i,1,n){
t2.del(1,p[i].x);t2.del(1,p[i].y);
t1.insert(1,p[i].x);t1.insert(1,p[i].y);
ans=min(ans,t1.calc()+t2.calc());
}
printf("%lld\n",ans+n+base);
}
}
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d%d",&k,&n);
char f1[5],f2[5];
rep(i,1,n){
scanf("%s %lld %s %lld",f1,&x[i],f2,&y[i]);
if(f1[0]==f2[0])base+=iabs(x[i]-y[i]),i--,n--;
}
if(k==1)solve1::main();
else if(n<=1000)solve2::main();
else solve3::main();
return 0;
}
感觉题略难与2014的(2014题实在太水了)
不知道今年的举办国是哪个啊,感觉有点虚