AMPPZ2014

[AMPPZ2014]The Lawyer

记录每天结束的最早的会议以及开始的最晚的会议即可。

#include<cstdio>
#define N 500010
int n,m,i,d,a[N],b[N],st[21],en[21];
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
int main(){
  for(read(n),read(m),i=1;i<=n;i++){
    read(a[i]),read(b[i]),read(d);
    if(!en[d]||b[en[d]]>b[i])en[d]=i;
    if(!st[d]||a[st[d]]<a[i])st[d]=i;
  }
  for(i=1;i<=m;i++)if(!st[i]||b[en[i]]>=a[st[i]])puts("NIE");else printf("TAK %d %d\n",en[i],st[i]);
  return 0;
}

  

[AMPPZ2014]Petrol

一遍spfa求出d[x]表示离x最近的加油站到x的距离。

对于每条边(x,y,w),将边权重置为d[x]+d[y]+w。

然后将边和询问按照权值从小到大排序,每次将所有边权小于等于询问的边加入,查询两点是否连通,用并查集维护。

#include<cstdio>
#include<algorithm>
const int N=200010,M=1048575,inf=~0U>>1;
int n,s,m,T,i,x,c[N],g[N],nxt[N<<1],v[N<<1],w[N<<1],ed,h=1,t,size,q[M+1],in[N],d[N],f[N],ans[N];
struct E{int x,y,z,id;}e[N],Q[N];
inline bool cmp(E a,E b){return a.z<b.z;}
inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
int main(){
  for(read(n),read(s),read(m),i=1;i<=s;i++)read(c[i]);
  for(i=1;i<=m;i++)read(e[i].x),read(e[i].y),read(e[i].z),add(e[i].x,e[i].y,e[i].z),add(e[i].y,e[i].x,e[i].z);
  for(i=1;i<=n;i++)d[i]=inf,f[i]=i;
  for(i=1;i<=s;i++)d[c[i]]=0,in[c[i]]=1,q[++t]=c[i],size++;
  while(size)for(i=g[x=q[h++]],h&=M,size--,in[x]=0;i;i=nxt[i])if(d[x]+w[i]<d[v[i]]){
    d[v[i]]=d[x]+w[i];
    if(!in[v[i]]){
      size++,in[v[i]]=1;
      if(d[v[i]]<d[q[h]])q[h=(h+M)&M]=v[i];else q[t=(t+1)&M]=v[i];
    }
  }
  for(i=1;i<=m;i++)if((long long)d[e[i].x]+d[e[i].y]<inf)e[i].z+=d[e[i].x]+d[e[i].y];else e[i].z=inf;
  for(read(T),i=1;i<=T;i++)read(Q[i].x),read(Q[i].y),read(Q[i].z),Q[i].id=i;
  std::sort(e+1,e+m+1,cmp),std::sort(Q+1,Q+T+1,cmp);
  for(i=x=1;i<=T;i++){
    while(x<=m&&e[x].z<=Q[i].z){
      if(F(e[x].x)!=F(e[x].y))f[f[e[x].x]]=f[e[x].y];
      x++;
    }
    ans[Q[i].id]=F(Q[i].x)==F(Q[i].y);
  }
  for(i=1;i<=T;i++)puts(ans[i]?"TAK":"NIE");
  return 0;
}

  

[AMPPZ2014]The Prices

f[S]=在一家店里购买S集合的价格的最小值

g[S]=购买S集合的价格的最小值

g[i]=min(f[j]+g[i^j]),j是i的子集

时间复杂度为$O(n2^m+3^m)$。

#include<cstdio>
int n,m,i,j,a[16],f[1<<16],g[1<<16],inf=1000000000;
inline void min(int&a,int b){if(a>b)a=b;}
void dfs(int x,int sum,int S){min(f[S],sum);for(;x<m;x++)dfs(x+1,sum+a[x],S|(1<<x));}
int main(){
  for(scanf("%d%d",&n,&m),i=1;i<(1<<m);i++)f[i]=inf;
  while(n--){for(scanf("%d",&j),i=0;i<m;i++)scanf("%d",&a[i]);dfs(0,j,0);}
  for(i=1;i<(1<<m);i++)for(g[j=i]=inf;j;j=(j-1)&i)min(g[i],f[j]+g[i^j]);
  return printf("%d",g[(1<<m)-1]),0;
}

  

[AMPPZ2014]Divisors

设f[i]=i出现次数,则ans=sum(f[i]*f[j],i|j)-n,时间复杂度为$O(n\log n)$。

#include<cstdio>
int n,i,j,k,f[2000001];long long ans;
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
int main(){
  for(read(n),ans=-n;n--;f[i]++)read(i),k=k>i?k:i;
  for(i=1;i<=k;i++)for(j=i;j<=k;j+=i)ans+=(long long)f[i]*f[j];
  return printf("%lld",ans),0;
}

  

[AMPPZ2014]Euclidean Nim

如果gcd(p,q)不整除n,则永远不会停止

若p==q,则p必胜

否则p,q,n/=gcd,假设p<q

若q是先手且n<q,则q必败

若p是先手且n>=p,则p必胜

若p是先手且n<p,则当(q-p)|n时p必败,否则p必胜

若q是先手且n>=q,设z=n%q,若(q-p)|z且z<p,则q必胜否则q必败

#include<cstdio>
int T,p,q,n,g;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
void work(){
  g=gcd(p,q);
  if(n%g){puts("R");return;}
  if(p==q){puts("E");return;}
  p/=g,q/=g,n/=g;
  if(p<q){
    if(n>=p){puts("E");return;}
    if(n%(q-p)==0)puts("P");else puts("E");
  }else{
    if(n<p){puts("P");return;}
    g=n%p;
    if(g%(p-q)==0&&g<q)puts("E");else puts("P");
  }
}
int main(){
  for(scanf("%d",&T);T--;work())scanf("%d%d%d",&p,&q,&n);
  return 0;
}

  

[AMPPZ2014]Pillars

首先无视障碍物构造一组解,然后根据障碍物的位置调整,时间复杂度为$O(nm+f)$。

#include<cstdio>
int n,m,f,i,j,x,y;char a[1010][1010];
int main(){
  scanf("%d%d%d",&n,&m,&f);
  for(i=1;i<=n;i++)for(j=1;j<=m;j++)a[i][j]=i&1?‘D‘:‘G‘;
  for(i=1;i<=n;i++){
    if(i<n)a[i][1]=‘P‘;
    if(i>1&&(i&1))a[i][2]=‘L‘;
    if(!(i&1))a[i][m]=‘L‘;
  }
  for(i=0;i<f;i++){
    scanf("%d%d",&x,&y);
    if(x&1){
      a[x+1][y-1]=‘L‘;
      a[x][y+2]=‘P‘;
      a[x+1][y+2]=‘P‘;
      a[x+2][y+3]=‘L‘;
    }else{
      if(y==3){
        a[x][1]=‘G‘;
        a[x][2]=‘P‘;
        a[x+1][2]=‘D‘;
        a[x+1][y+2]=‘L‘;
      }else{
        a[x+1][y+2]=‘L‘;
        a[x][y-1]=‘P‘;
        a[x+1][y-1]=‘P‘;
        a[x+2][y-2]=‘L‘;
      }
    }
  }
  for(puts("TAK"),x=y=1,n=n*m-4*f;n--;){
    putchar(a[x][y]);
    if(a[x][y]==‘L‘)x--;
    else if(a[x][y]==‘P‘)x++;
    else if(a[x][y]==‘D‘)y--;
    else y++;
  }
  return 0;
}

  

[AMPPZ2014]Global Warming

[l1[i],r1[i]]里i是唯一的最小值,[l2[i],r2[i]]里i是唯一的最大值,用单调队列$O(n)$求出。

枚举最小值的位置i,则最大值的位置j须满足l1[i]<=j<=r1[i],l2[j]<=l1[i],r2[j]>=i

求出满足条件的最大的r2[j],则此时区间长度为min(r1[i],r2[j])-l1[i]+1,开头为l1[i]

排序后用线段树维护,然后将l1[i]与l2[i]交换、r1[i]与r2[i]交换再求一次即可求出最优解

时间复杂度为$O(n\log n)$。

#include<cstdio>
const int N=500010,M=1048577;
int n,i,j,a[N],q[N],t,l1[N],r1[N],l2[N],r2[N],len=1,st=1,v[M];
struct E{int v,w;E*nxt;}pool[N<<1],*cur=pool,*g[N],*h[N],*p;
inline void addg(int x,int y,int z){p=cur++;p->v=y;p->w=z;p->nxt=g[x];g[x]=p;}
inline void addh(int x,int y,int z){p=cur++;p->v=y;p->w=z;p->nxt=h[x];h[x]=p;}
inline void read(int&a){
  char c;bool f=0;a=0;
  while(!((((c=getchar())>=‘0‘)&&(c<=‘9‘))||(c==‘-‘)));
  if(c!=‘-‘)a=c-‘0‘;else f=1;
  while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;
  if(f)a=-a;
}
inline void swap(int&a,int&b){int c=a;a=b;b=c;}
inline void max(int&a,int b){if(a<b)a=b;}
inline void ins(int c,int d){
  int x=1,a=1,b=n,mid;
  while(1){
    max(v[x],d);
    if(a==b)return;
    mid=(a+b)>>1,x<<=1;
    if(c<=mid)b=mid;else x|=1,a=mid+1;
  }
}
void ask(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){max(j,v[x]);return;}
  int mid=(a+b)>>1;
  if(c<=mid)ask(x<<1,a,mid,c,d);
  if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
void work(){
  for(i=1;i<=n;i++)addg(l1[i],i,r1[i]),addh(l2[i],i,r2[i]);
  for(i=1;i<=n;i++){
    for(p=h[i];p;p=p->nxt)ins(p->v,p->w);
    for(p=g[i];p;p=p->nxt){
      j=0,ask(1,1,n,i,p->w);
      if(j<p->v)continue;
      if((t=(p->w<j?p->w:j)-i+1)>len)len=t,st=i;else if(t==len&&i<st)st=i;
    }
  }
}
int main(){
  for(read(n),i=1;i<=n;i++)read(a[i]);
  for(q[t=0]=0,i=1;i<=n;q[++t]=i++){
    while(t&&a[q[t]]>a[i])t--;
    l1[i]=q[t]+1;
  }
  for(q[t=0]=n+1,i=n;i;q[++t]=i--){
    while(t&&a[q[t]]>a[i])t--;
    r1[i]=q[t]-1;
  }
  for(q[t=0]=0,i=1;i<=n;q[++t]=i++){
    while(t&&a[q[t]]<a[i])t--;
    l2[i]=q[t]+1;
  }
  for(q[t=0]=n+1,i=n;i;q[++t]=i--){
    while(t&&a[q[t]]<a[i])t--;
    r2[i]=q[t]-1;
  }
  work();
  for(cur=pool,i=1;i<=n;i++)g[i]=h[i]=NULL;
  for(i=1;i<M;i++)v[i]=0;
  for(i=1;i<=n;i++)swap(l1[i],l2[i]),swap(r1[i],r2[i]);
  work();
  return printf("%d %d",len,st),0;
}

  

[AMPPZ2014]Hit of the Season

不会。

[AMPPZ2014]The Staging

首先求出所有的环,破环成链,每个环互相独立

对于一个环,要求出最终存活的人数,则需要先找到第一个开枪的人x

那么第x+1个人一定不能开枪,而且一定不能存活

所以需要查询区间[x+1,x+len]中,左端点的人不开枪且不存活时的存活人数

用线段树维护每个环,每个节点维护如下信息:

l,r:区间左右端点

val.x区间内开枪时间的最小值

val.y区间内开枪时间的最小值来自哪里

v[i][j]l开枪状态为i,存活状态为j时的存活人数

s[i][j]l开枪状态为i,存活状态为j时r的存活状态

时间复杂度为$O(q\log n)$。

#include<cstdio>
#define N 200010
int n,m,q,i,j,x,y,p[N],u[N<<1],pos[N<<1],flag;
int cnt,from[N],loc[N],st[N],len[N],rem[N],all;
struct PI{
  int x,y;
  PI(){}
  PI(int _x,int _y){x=_x,y=_y;}
  inline PI operator+(PI b){return x<b.x?PI(x,y):b;}
}val[1048577],fir;
inline void changeu(int x,int y){
  val[x=pos[x]].x=y;
  for(x>>=1;x;x>>=1)val[x]=val[x<<1]+val[x<<1|1];
}
void asku(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){
    if(flag)fir=fir+val[x];else flag=1,fir=val[x];
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)asku(x<<1,a,mid,c,d);
  if(d>mid)asku(x<<1|1,mid+1,b,c,d);
}
struct P{
  int l,r,v[2][2],f[2][2];
  P(){}
  P(int x){
    l=r=x;
    v[0][0]=v[1][0]=f[0][0]=f[0][1]=0;
    v[0][1]=v[1][1]=f[1][0]=f[1][1]=1;
  }
  inline P operator+(P b){
    P c;
    c.l=l,c.r=b.r;
    c.v[0][0]=v[0][0]+b.v[u[b.l]<u[r]||!f[0][0]][!f[0][0]];
    c.v[0][1]=v[0][1]+b.v[u[b.l]<u[r]||!f[0][1]][!f[0][1]];
    c.v[1][0]=v[1][0]+b.v[u[b.l]<u[r]||!f[1][0]][!f[1][0]];
    c.v[1][1]=v[1][1]+b.v[u[b.l]<u[r]||!f[1][1]][!f[1][1]];
    c.f[0][0]=b.f[u[b.l]<u[r]||!f[0][0]][!f[0][0]];
    c.f[0][1]=b.f[u[b.l]<u[r]||!f[0][1]][!f[0][1]];
    c.f[1][0]=b.f[u[b.l]<u[r]||!f[1][0]][!f[1][0]];
    c.f[1][1]=b.f[u[b.l]<u[r]||!f[1][1]][!f[1][1]];
    return c;
  }
}v[1048577],ans;
void build(int x,int a,int b){
  if(a==b){
    pos[a]=x;
    v[x]=P(a),val[x]=PI(u[a],a);
    return;
  }
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b);
  v[x]=v[x<<1]+v[x<<1|1],val[x]=val[x<<1]+val[x<<1|1];
}
void ask(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){
    if(flag)ans=ans+v[x];else flag=1,ans=v[x];
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)ask(x<<1,a,mid,c,d);
  if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
inline void change(int x,int y){
  u[x]=y;
  for(x=pos[x]>>1;x;x>>=1)v[x]=v[x<<1]+v[x<<1|1];
}
inline void query(int x){//查询第x个环的答案
  all-=rem[x];
  flag=0,asku(1,1,m,st[x],st[x]+len[x]-1);
  flag=0,ask(1,1,m,fir.y+1,fir.y+len[x]);
  rem[x]=ans.v[0][0];
  all+=rem[x];
}
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
int main(){
  read(n);m=n*2;
  for(i=1;i<=n;i++)read(p[i]);
  for(i=0,j=1;j<=n;j++)if(!loc[j]){
    st[++cnt]=loc[j]=++i,len[cnt]=1,from[j]=cnt;
    for(x=p[j];x!=j;x=p[x])loc[x]=++i,len[cnt]++,from[x]=cnt;
    i+=len[cnt];
  }
  for(i=1;i<=n;i++)read(x),u[loc[i]]=u[loc[i]+len[from[i]]]=x;
  build(1,1,m);
  for(i=1;i<=cnt;i++)query(i);
  printf("%d\n",all);
  for(read(q);q--;printf("%d\n",all)){
    read(x),read(y);
    changeu(loc[x],y);
    change(loc[x],y),change(loc[x]+len[from[x]],y);
    query(from[x]);
  }
  return 0;
}

  

[AMPPZ2014]The Cave

1号点到答案点的距离为$\max(0,\frac{dis(1,a_i)+dis(1,b_i)-d_i}{2})$,

找到距离最大的那条限制,则如果有解,那么满足那条限制的离1号点最近的点一定可行。

时间复杂度为$O(n+m)$。

#include<cstdio>
#define N 300010
int T,n,m,i,x,y,t,g[N],nxt[N<<1],v[N<<1],ed,a[N],b[N],d[N],dis[3][N];
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,int y,int z,int p){
  dis[p][x]=z++;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x,z,p);
}
int main(){
  for(read(T);T--;){
    read(n),read(m);
    for(ed=0,i=1;i<=n;i++)g[i]=0;
    for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
    for(i=1;i<=m;i++)read(a[i]),read(b[i]),read(d[i]);
    for(dfs(1,0,0,0),x=0,i=1;i<=m;i++){
      t=dis[0][a[i]]+dis[0][b[i]]-d[i];
      if(!x||t>y)x=i,y=t;
    }
    dfs(a[x],0,0,1),dfs(b[x],0,0,2);
    for(t=0,i=1;i<=n;i++)if(dis[1][i]+dis[2][i]<=d[x])if(!t||dis[0][i]<y)t=i,y=dis[0][i];
    if(!t){puts("NIE");continue;}
    for(dfs(t,0,0,0),i=1;i<=m;i++)if(dis[0][a[i]]+dis[0][b[i]]>d[i]){t=0;break;}
    if(t)printf("TAK %d\n",t);else puts("NIE");
  }
  return 0;
}

  

[AMPPZ2014]The Captain

考虑代价为|a.x-b.x|的情况,将点按x排序后,只有相邻两个点之间的边有用。

于是连出4n条边,然后跑Dijkstra,求出1到n的最短路即可。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=200010,M=1048575,inf=~0U>>1;
int n,i,x,g[N],nxt[N<<2],v[N<<2],w[N<<2],ed,d[N];
struct P{int x,y,id;}a[N];
inline bool cmpx(P a,P b){return a.x<b.x;}
inline bool cmpy(P a,P b){return a.y<b.y;}
inline int abs(int x){return x>0?x:-x;}
inline void add(int x,int y,int z){
  v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;
  v[++ed]=x;w[ed]=z;nxt[ed]=g[y];g[y]=ed;
}
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
struct PI{
  int x,y;
  PI(){}
  PI(int _x,int _y){x=_x,y=_y;}
  inline PI operator+(PI b){return x<b.x?PI(x,y):b;}
}val[524289];
void build(int x,int a,int b){
  val[x]=PI(inf,a);
  if(a==b)return;
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b);
}
inline void change(int x,int a,int b,int c,int d){
  if(a==b){val[x].x=d;return;}
  int mid=(a+b)>>1;
  c<=mid?change(x<<1,a,mid,c,d):change(x<<1|1,mid+1,b,c,d);
  val[x]=val[x<<1]+val[x<<1|1];
}
int main(){
  for(read(n),i=1;i<=n;i++)read(a[i].x),read(a[i].y),a[i].id=i;
  for(sort(a+1,a+n+1,cmpx),i=1;i<n;i++)if(a[i+1].x-a[i].x<=abs(a[i].y-a[i+1].y))add(a[i].id,a[i+1].id,a[i+1].x-a[i].x);
  for(sort(a+1,a+n+1,cmpy),i=1;i<n;i++)if(a[i+1].y-a[i].y<=abs(a[i].x-a[i+1].x))add(a[i].id,a[i+1].id,a[i+1].y-a[i].y);
  for(i=2;i<=n;i++)d[i]=inf;
  build(1,1,n),change(1,1,n,1,0);
  while(val[1].x<inf)for(change(1,1,n,x=val[1].y,inf),i=g[x];i;i=nxt[i])if(d[x]+w[i]<d[v[i]])change(1,1,n,v[i],d[v[i]]=d[x]+w[i]);
  return printf("%d",d[n]),0;
}

  

时间: 2024-08-02 11:39:09

AMPPZ2014的相关文章

【BZOJ 4148】 4148: [AMPPZ2014]Pillars (乱搞)

4148: [AMPPZ2014]Pillars Time Limit: 5 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 100  Solved: 49 Description 给定一个n*m的矩形,其中有f个2*2的障碍物,其中任意两个障碍物中心之间的欧几里得距离至少为6, 且每个障碍物的中心到边缘的距离至少为3.请找到一条从左下角(1,1)出发经过所有没有障碍物的点各 一次的且最后回到左下角的回路. Input 第一行包含三个整数n,

BZOJ 4144: [AMPPZ2014]Petrol

4144: [AMPPZ2014]Petrol Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 457  Solved: 170[Submit][Status][Discuss] Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走

循环队列+堆优化dijkstra最短路 BZOJ 4152: [AMPPZ2014]The Captain

循环队列基础知识 1.循环队列需要几个参数来确定 循环队列需要2个参数,front和rear 2.循环队列各个参数的含义 (1)队列初始化时,front和rear值都为零: (2)当队列不为空时,front指向队列的第一个元素,rear指向队列最后一个元素的下一个位置: (3)当队列为空时,front与rear的值相等,但不一定为零: 3.循环队列入队的伪算法 (1)把值存在rear所在的位置: (2)rear=(rear+1)%maxsize ,其中maxsize代表数组的长度: 4.循环队列

bzoj4144 [AMPPZ2014]Petrol 图论 最短路 并查集

bzoj4144 [AMPPZ2014]Petrol 图论 最短路 并查集 1.这道题我们主要就是要求出距离一个油站的最近的油站 首先我们dijkstra 求出任意一个点到 离他最近的油站的距离 2.然后会发现 如果一条边的两个端点 的最近油站不同的话 那么这条边就会在这两个油站的最短路上 3.然后对于每一条边 我们将他的权值 变为 dis[ u ] + dis[ v ] + e[ i ][ j ] 如果u与v最近油站相同 那么这个无意义 如果不同 那么久表示 u 最近油站 到 v 最近油站的最

【BZOJ】【4144】【AMPPZ2014】Petrol

最短路+最小生成树+倍增 图论问题中综合性较强的一题= =(Orz vfk) 比较容易发现,关键的还是有加油站的这些点,其他点都是打酱油的. 也就是说我们重点是要求出 关键点之间的最短路. 这玩意……如果枚举加油站所在的点,然后跑单源最短路什么的……肯定TLE啊. 我们记from[i]表示离 i 最近的关键点,仔细考虑一下,A->B的最短路径上,一定是前一半的from[i]为A,然后走过某条路以后,后一半的from[i]为B.为什么呢?我们不妨设中间出现了一个点x的from[x]=C,那么我们大

【BZOJ】4147: [AMPPZ2014]Euclidean Nim

[算法]博弈论+数论 [题意]给定n个石子,两人轮流操作,规则如下: 轮到先手操作时:若石子数<p添加p个石子,否则拿走p的倍数个石子 : 轮到后手操作时:若石子数<q添加q个石子,否则拿走q的倍数个石子 . 拿走所有石子的人胜利,问先手是否必胜,或输出游戏会永远进行下去 [题解]引用自:BZOJ 4147 AMPPZ2014 Euclidean Nim 博弈论+数论 by popoqqq 施工中--

【BZOJ】【4145】【AMPPZ2014】The Prices

状压DP/01背包 Orz Gromah 容易发现m的范围很小……只有16,那么就可以状压,用一个二进制数来表示买了的物品的集合. 一种简单直接的想法是:令$f[i][j]$表示前$i$个商店买了状态集合为$j$的商品的最小代价,那么我们转移的时候就需要枚举在第$i$个商店买了哪些商品吗,这样的话带上枚举子集,复杂度就会变成$O(n*3^m)$,并不是我们能够忍受的…… 那么怎么搞呢?我们每次转移的时候,不再枚举子集,而是搞一个类似01背包的东西:(以下来自Gromah) 我们首先令$f[i][

【BZOJ】【4146】 【AMPPZ2014】Divisors

暴力 由于值的范围很小($ \leq 2*10^6$),所以用一个cnt数组统计每个值有多少个数,然后从小到大,统计每个数的倍数即可. 根据调和数?的神奇性质= =这样是$O(nlogn)$的…… 1 /************************************************************** 2 Problem: 4146 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:4644 ms 7 Mem

bzoj4143: [AMPPZ2014]The Lawyer

4143: [AMPPZ2014]The Lawyer Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 462  Solved: 257[Submit][Status][Discuss] Description Byteasar要制订m天的会议计划,一共有n场会议,第i场会议开始于第d[i]天的第a[i]秒,结束于第d[i]天的第b[i]秒. 对于每一天,请找出这一天的两场会议i,j,使得它们不冲突,即不存在一个

BZOJ 4143: [AMPPZ2014]The Lawyer( sort )

水题... 排序搞出每天的会议有哪些, 然后再按照会议的开始时间和结束时间排序, 最晚开始的和最早结束的会议不是同一场而且最晚开始的时间>最早结束的会议就有可能方案 ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostrea