1. 程式人生 > >day2018.11.6模擬賽總結

day2018.11.6模擬賽總結

頹了這麼多天,終於重新開始寫模擬賽blog了.

誒今天是模擬賽day幾來著???


T1:

題目大意:給定一個矩陣表示A,若A_{i,j}為0表示i,j為空地,為1表示障礙.再給出一個移動序列LRUD分別表示向左向右向上向下.問最少去掉幾個移動序列的字元可以使得移動不出邊界且不經過障礙.

考場得分:100.

資料可以支援O(n^3)的演算法,所以考慮DP,而且很顯然這是一個DP,所以直接設f[k][i][j]表示移動序列上到第k個字元,目前在網格上的座標為(i,j)的最多保留字元數量.

發現狀態k只會影響到狀態k+1,所以可以用f[k][i][j]更新狀態f[k+1][i][j]f[k+1][i+x[k+1]][j+y[k+1]],並用滾動陣列優化.

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=400,INF=(1<<29)-1;

int ri(){
  int x=0;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar());
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x;
}

char rc1(){
  char c=getchar();
  while (c<'0'||c>'9') c=getchar();
  return c;
} 

char rc2(){
  char c=getchar();
  while (c<'A'||c>'Z') c=getchar();
  return c;
}

int f[2][N+9][N+9],use[2][N+9][N+9],old,now,ans;
int b[N+9][N+9],x[N+9],y[N+9],n,m,k;

Abigail into(){
  n=ri();m=ri();k=ri();
  for (int i=1;i<=n;++i)
    for (int j=1;j<=m;++j)
      b[i][j]=rc1()-'0';
  for (int i=1;i<=k;++i)
    switch (rc2()){
      case 'L':
      	x[i]=0;y[i]=-1;
        break;
      case 'R':
      	x[i]=0;y[i]=1;
      	break;
      case 'U':
      	x[i]=-1;y[i]=0;
        break;
      case 'D':
      	x[i]=1;y[i]=0;
      	break;
    }
}

Abigail work(){
  for (int i=1;i<=n;++i)
    b[i][0]=b[i][m+1]=1;
  for (int i=1;i<=m;++i)
    b[0][i]=b[n+1][i]=1;
  old=1;now=0;
  use[old][1][1]=1;
  f[old][1][1]=0;
  for (int g=0;g<k;++g){
    for (int i=1;i<=n;++i)
      for (int j=1;j<=m;++j)
        use[now][i][j]=f[now][i][j]=0;
    for (int i=1;i<=n;++i)
      for (int j=1;j<=m;++j)
        if (use[old][i][j]){
          f[now][i][j]=max(f[now][i][j],f[old][i][j]);
          use[now][i][j]=1;
          if (b[i+x[g+1]][j+y[g+1]]) continue;
          use[now][i+x[g+1]][j+y[g+1]]=1;
          f[now][i+x[g+1]][j+y[g+1]]=max(f[now][i+x[g+1]][j+y[g+1]],f[old][i][j]+1);
        }
    now^=1;old^=1;
  }
  for (int i=1;i<=n;++i)
    for (int j=1;j<=m;++j)
      if (use[old][i][j]) ans=max(ans,f[old][i][j]);
}

Abigail outo(){
  printf("%d\n",k-ans);
}

int main(){
  freopen("island.in","r",stdin);
  freopen("island.out","w",stdout);
  into();
  work();
  outo();
  return 0;
} 

 

T2:

題目大意:給出一串序列,現在問這個序列的第k小區間和為多少.

考場得分:100分.

考場想了一小時,寫了5分鐘...

其實看到這種沒什麼數學模型,與資料結構和圖論明顯沒什麼關係的題就可以直接考慮DP和分治了.然後這道題DP想不到任何有用的性質,就考慮二分了.

很自然地想到二分第k小的區間和,然後用一個O(n^2)的暴力列舉區間來判定,優秀地做到了O(n^2logSUM)比直接列舉區間還要慢的演算法.

考慮優化,我們考慮列舉區間的右端點r,很容易發現若左端點為l時滿足條件,那麼屬於區間[l,r-1]的區間一定都滿足條件,所以考慮二分維護,可以做到優秀的O(nlog^2n)的演算法.

然而這樣還是不能做到滿分.那麼考慮列舉右端點r

時,左端點l也一直往右移,那麼直接維護l即可.時間複雜度O(nlogn).

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=1000000;
const LL INF=(1LL<<62)-1LL;

LL a[N+9],sum[N+9],ans,k;
int n;

int ri(){
  int x=0;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar());
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x;
}

int rl(){
  LL x=0;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar());
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x;
}

bool check(LL mid){
  int last=1;LL ans=0;
  for (int i=1;i<=n;++i){
    while (sum[i]-sum[last-1]>mid) last++;
    ans+=i-last+1;
  }
  return ans>=k;
}

Abigail into(){
  n=ri();k=rl();
  for (int i=1;i<=n;++i){
    a[i]=rl();
    sum[i]=sum[i-1]+a[i];
  }
}

Abigail work(){
  ans=INF;
  for (int i=61;i>=0;--i)
    if (check(ans-(1LL<<i))) ans-=1LL<<i;
}

Abigail outo(){
  printf("%lld\n",ans);
}

int main(){
  freopen("movie.in","r",stdin);
  freopen("movie.out","w",stdout);
  into();
  work();
  outo();
  return 0;
} 

 

T3:

題目大意:給定一棵樹,以及若干點集,要求將點集中的點分成幾個組,每個組只有兩個點,使得每組的兩點在樹上的距離最小是否小於樹的總點數,若小於輸出方案.

考場得分:35分.(寫鏈的部分分說是15分實際有35分賊開心^o^)

這道題貌似跟藍書上的異象石很像啊,都用到了同一個結論.(然而我還是想了1個小時都不會)

首先我們考慮鏈的部分分,就可以發現距離一定不會超過樹的總點數,並且一定是相鄰兩點分一組.

然後我們像異象石這題的做法一樣,將所有點按照dfs序來排序,就可以發現:

\sum_{i}^{s} dis(a_i,a_i+1)\leq 2(n-1)

其中ai為詢問的點集,並且設a_{s+1}=a_1.

所以兩點之間連邊肯定是取排序後相鄰兩點,那麼就只會有兩種不同的方案,在這兩種方案裡面必定有一種距離和小於等於n-1,那麼取小的那種就可以了.

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=200000;

struct side{
  int y,next;
}e[N*2+9];
int lin[N+9],top,n;

void ins(int x,int y){
  e[++top].y=y;
  e[top].next=lin[x];
  lin[x]=top;
}

struct node{
  int deep,dfn,top,fa,size,son;
}d[N+9];
int num=0;

void dfs1(int k,int fa){
  d[k].fa=fa;
  d[k].deep=d[fa].deep+1;
  d[k].size=1;
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^fa){
      dfs1(e[i].y,k);
      d[k].size+=d[e[i].y].size;
      if (d[d[k].son].size<d[e[i].y].size) d[k].son=e[i].y;
    }
}

void dfs2(int k,int top){
  d[k].top=top;
  d[k].dfn=++num;
  if (d[k].son) dfs2(d[k].son,top);
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^d[k].fa&&e[i].y^d[k].son) dfs2(e[i].y,e[i].y);
}

int LCA(int x,int y){
  while (d[x].top^d[y].top)
    d[d[x].top].deep>d[d[y].top].deep?x=d[d[x].top].fa:y=d[d[y].top].fa;
  return d[x].deep<d[y].deep?x:y;
}

int s,tmp[N+9];

bool cmp(const int &a,const int &b){return d[a].dfn<d[b].dfn;}

Abigail into(){
  scanf("%d",&n);
  int x,y;
  for (int i=1;i<n;++i){
    scanf("%d%d",&x,&y);
    ins(x,y);ins(y,x);
  }
}

Abigail work(){
  dfs1(1,0);
  dfs2(1,1);
}

Abigail getans(){
  #define dis(a,b) d[a].deep+d[b].deep-2*d[LCA(a,b)].deep
  while (~scanf("%d",&s)&s){
    for (int i=1;i<=s;++i)
      scanf("%d",&tmp[i]);
    sort(tmp+1,tmp+1+s,cmp);
    int sum=0;
    sum+=dis(tmp[1],tmp[s]);
    for (int i=2;i<s;i+=2)
      sum+=dis(tmp[i],tmp[i+1]);
    puts("Yes");
    if (sum<n){
      printf("%d %d\n",tmp[1],tmp[s]);
      for (int i=2;i<s;i+=2)
        printf("%d %d\n",tmp[i],tmp[i+1]);
    }else{
      for (int i=1;i<s;i+=2)
       printf("%d %d\n",tmp[i],tmp[i+1]);
    }
  }
}

int main(){
  freopen("kieru.in","r",stdin);
  freopen("kieru.out","w",stdout);
  into();
  work();
  getans();
  return 0;
}