day2018.6.24模拟赛总结

这场比赛DP只有100分,T2的DP写挂了...

没事我还有T1特别稳的100分在...

论数据为何如此之水...


T1:

题目大意:

从前有个湖.湖边有n座城,顺时针方向编号1-n.每个城用ai描述,ai可正可负.从前有个小B,他有个初始血量为1的血槽。现在他将从某个城市出发,沿顺时针方向遍历这n个城市(不回到出发地).小B经过第i个城市的时候血量将会增加ai.若a5=-2那么经过5号城市的时候,小B的血量将会减少2个单位.

小B的血量一定是正数,否则小B不安全.请求出有多少个出发地使得小B安全地走过n个城市.

注:1<=n<=10^6.

对这道题其实很简单,很多数据结构都可以做.

但是要卡时,所以我一直在想O(n)的做法,害怕线段树被卡,ST表又不敢写细节太多而且好像不可做...

扫描二维码关注公众号,回复: 1769881 查看本文章

没事先写n^2暴力.

写完之后就去写T2了,之后再跑回来写T1发现就是个傻逼单调队列...

先把数列复制一份,我们可以发现答案就是枚举1~n每个点.

那么从点i开始满足条件就是a[i],a[i]+a[i+1],...,a[i]+a[i+1]+...+a[i+n-1]的最小值大于等于0...

那么就用一个前缀和数组A[n]保存前缀和,假设当前枚举到了i,那么记录一个num表示-a[1]-a[2]-a[3]-...-a[i-1].

那么就用一个单调队列可以O(1)完成从i开始的答案转移到从i+1开始的答案...

然后就好办了...

考场AC代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
typedef long long LL;
const int N=1000000;
LL a[2*N+2],A[2*N+2],num;
int h,t,n,sum;
struct node{
  LL num;
  int x;
}q[N*3+3];
ACF into(){
  scanf("%d",&n);
  for (int i=1;i<=n;i++){
    scanf("%lld",&a[i]);
    A[i]=a[i]+A[i-1];
  }
  for (int i=1;i<=n;i++){
    a[i+n]=a[i];
    A[i+n]=a[i+n]+A[i+n-1];
  } 
}
void push(LL num,int x){
  while (q[t].num>num&&t>=h) t--;
  q[++t].num=num;q[t].x=x;
}
void pop(int x){
  if (q[h].x==x) h++;
}
LL top(){
  return q[h].num;
}
ACF work(){
  h=1;t=0;
  for (int i=1;i<=n;i++) push(A[i],i);
  for (int i=1;i<=n;i++){
    if (top()+num>=0LL) sum++;
    num-=a[i];
    pop(i);
    push(A[i+n],i+n);
  }
}
ACF outo(){
  printf("%d\n",sum);
}
int main(){
  //freopen("sum.in","r",stdin);
  //freopen("sum.out","w",stdout);
  into();
  work();
  outo();
  return 0;
}


T2:

题目大意:给定一张有向无环图,找出A到B的路径长度恰好是K的倍数的路径。这些路径中,最短的是多长呢?

注:n为点的数量,k为题目中的K,m为边的数量,vi为第i条边的权值,则1<=n,k<=1000,1<=m<=20000,1<=vi<=100000.

我在考场上的做法是一个DP,但写挂了...

思路就是用f[i][j]表示从A到i的路径中,边权和mod k后为j的最短路径的长度/k.

emmm思路大概没什么问题,考场代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
const int N=1000;
const int M=20000;
const int INF=1000000000;
struct side{
  int y,next,v;
}e[M+5];
int lin[N+5],f[N+5][N+5],n,m,k,A,B,top,hh,hg,cnt[N+5],t;
bool use[N+1];
queue < int > q;
void ins(int X,int Y,int V){
  e[++top].y=Y;e[top].v=V;
  e[top].next=lin[X];
  lin[X]=top;
}
ACF into(){
  scanf("%d%d%d%d%d",&n,&m,&k,&A,&B);
  int x,y,z;
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&z);
    ins(x,y,z);
    cnt[y]++;
  }
}
void topsort(){
  for (int i=1;i<=n;i++)
    if (!cnt[i]) q.push(i);
  while (!q.empty()){
    for (int i=lin[q.front()];i;i=e[i].next){
      t=q.front();
      for (int j=0;j<k;j++){
      	hh=j+e[i].v;hg=f[t][j];
      	if (hh>=k) hg++,hh-=k;
      	f[e[i].y][hh]=min(f[e[i].y][hh],hg);
      }
      cnt[e[i].y]--;
      if (cnt[e[i].y]) continue;
      q.push(e[i].y);
    }
    q.pop();
  }
}
ACF work(){
  for (int i=0;i<N+5;i++)
    for (int j=0;j<N+5;j++)
      f[i][j]=INF;
  f[A][0]=0;
  topsort();
}
ACF outo(){
  if (f[B][0]!=INF) printf("%d\n",k*f[B][0]);
  else printf("-1\n");
}
int main(){
  //freopen("shortk.in","r",stdin);
  //freopen("shortk.out","w",stdout);
  into();
  work();
  outo();
  return 0;
}

之后发现各种错误,先是把某个初始化写错了,然后是把某个东西用减法代替了除法和取模,还把min给漏掉了,然后INF还开太大了...

所以,AC代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
const int N=1000;
const int M=20000;
const int INF=1000000000;
struct side{
  int y,next,v;
}e[M+5];
int lin[N+5],f[N+5][N+5],n,m,k,A,B,top,hh,hg,cnt[N+5],t;
bool use[N+1];
queue < int > q;
void ins(int X,int Y,int V){
  e[++top].y=Y;e[top].v=V;
  e[top].next=lin[X];
  lin[X]=top;
}
ACF into(){
  scanf("%d%d%d%d%d",&n,&m,&k,&A,&B);
  int x,y,z;
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&z);
    ins(x,y,z);
    cnt[y]++;
  }
}
void topsort(){
  for (int i=1;i<=n;i++)
    if (!cnt[i]) q.push(i);
  while (!q.empty()){
    for (int i=lin[q.front()];i;i=e[i].next){
      t=q.front();
      for (int j=0;j<k;j++){
      	hh=j+e[i].v;hg=f[t][j]+hh/k;hh=hh%k;
      	f[e[i].y][hh]=min(f[e[i].y][hh],hg);
      }
      cnt[e[i].y]--;
      if (cnt[e[i].y]) continue;
      q.push(e[i].y);
    }
    q.pop();
  }
}
ACF work(){
  for (int i=0;i<N+5;i++)
    for (int j=0;j<N+5;j++)
      f[i][j]=INF;
  f[A][0]=0;
  topsort();
}
ACF outo(){
  if (f[B][0]!=INF) printf("%d\n",f[B][0]*k);
  else printf("-1\n");
}
int main(){
  //freopen("shortk.in","r",stdin);
  //freopen("shortk.out","w",stdout);
  into();
  work();
  outo();
  return 0;
}


T3:

我竟然被一颗线段树搞死了...

题目大意:给定一个数列Ai,长度为N.Ai为[1,K]范围内的整数.

M次操作,分为2种:

1、修改操作.修改数列中的单个元素.

2、询问操作.求数列A最短子段(包含元素个数最少且元素连续),使得子段包含[1,K]中每一个整数.

注:1<=N,M<=100000,1<=K<=50.

不会不会~_~...暴力都不会~_~...

论这颗线段树该怎么维护???随便维护...

正解的意思就是用一个long long数存下数集的状态.

我们可以发现一点的是维护一个前缀状态(即表示的是1~i联合起来的状态)改变只有k次.

所以我们每个节点维护一个前缀k数组和后缀k数组,数组中的第i项存k改变的位置以及改变后的k的数值.

注:这个前后缀是从这个节点的左右端点开始的.

然后就可以合并了,修改也很简单.

只不过代码长一点很多...

查询的话我们需要多存一个信息mi,表示最短长度,然后一个节点的mi可以从左右儿子的mi和中间部分维护两个指针用O(k)时间求出.

具体看代码:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
#define F first
#define S second
const int K=50,N=262144;
const int INF=1e9;
const int half=131072;
typedef long long LL;
int wan,k,n,q;
typedef pair< LL , int > P;
struct tree{
  int len,ans;
  P pref[K+1],suff[K+1];
  tree(){      //初始化 
    ans=INF;len=0;
  };
  tree(int t,int v){      //线段树底层修改 
    len=1;
    pref[0]=suff[0]=P(1LL<<v,t);
    ans=INF;
  }
}tr[N+1];
bool in(LL a,LL b){      //判断a是否包含在b中 
  return (a&b)==a;
}
void merge(tree &t,tree &l,tree &r){
  int pref_len=0,suff_len=0;
  for (int i=0;i<l.len;i++)
    t.pref[pref_len++]=l.pref[i];      //用左端点更新前缀
  for (int i=0;i<r.len;i++)      //用右端点更新前缀
    if (!pref_len||!in(r.pref[i].F,t.pref[pref_len-1].F)){      //若前面未包含 
      t.pref[pref_len]=r.pref[i]; 
      if (pref_len>0) t.pref[pref_len].F|=t.pref[pref_len-1].F;
      pref_len++;
    }
  suff_len=0;      //同上 
  for (int i=0;i<r.len;i++)
    t.suff[suff_len++]=r.suff[i];
  for (int i=0;i<l.len;i++)
    if (!suff_len||!in(l.suff[i].F,t.suff[suff_len-1].F)){
      t.suff[suff_len]=l.suff[i];
      if (suff_len>0) t.suff[suff_len].F|=t.suff[suff_len-1].F;
      ++suff_len;
    }
  assert(pref_len==suff_len);      //用中间部分更新答案 
  t.len=pref_len;
  t.ans=INF;
  int pref_pos=0;
  for (int suff_pos=l.len-1;suff_pos>=0;suff_pos--) {       
    while (pref_pos<r.len&&(l.suff[suff_pos].F|r.pref[pref_pos].F)!=(1LL<<k)-1)
    ++pref_pos;
    if (pref_pos<r.len) {
      LL curr_mask=l.suff[suff_pos].F|r.pref[pref_pos].F;
      if (curr_mask^(1LL<<k)-1) continue;
      t.ans=min(t.ans,r.pref[pref_pos].S-l.suff[suff_pos].S+1);
    }
  }
  t.ans=min(t.ans,min(l.ans,r.ans));      //用左右儿子更新 
}
void change(int k,int v){      //注:用zkw线段树写的
  k+=half;
  tr[k]=tree(k-half,v);
  for (k>>=1;k;k>>=1)
    merge(tr[k],tr[k<<1],tr[k<<1|1]);
}
int query(){
  return tr[1].ans;
}
ACF into(){
  scanf("%d%d%d",&n,&k,&q);
  int v;
  for (int i=0;i<n;i++){
    scanf("%d",&v);
    --v;
    change(i,v);
  }
}
ACF work(){
  int t,x,v,ans;
  for (int i=1;i<=q;i++){
    scanf("%d",&t);
    if (t==1){
      scanf("%d%d",&x,&v);
      change(--x,--v);
    }else{
      ans=query();
      if (ans==INF) printf("-1\n");
      else printf("%d\n",ans);
    }
  }
}
ACF outo(){
}
int main(){
  //freopen("short.in","r",stdin);
  //freopen("short.out","w",stdout);
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/80790665