Codeforces Round #699 (Div. 2)

A

题目大意,给一个操作序列,问能否通过删除一些操作使你从\((0,0)\)走向\((x,y)\)

只保留符号相同的操作,模拟一遍看看能不能达到给定的坐标。

#include <bits/stdc++.h>
#define N 1000009
using namespace std;
typedef long long ll;
int x,y,n;
char s[N];
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
int main(){
   int T=rd();
   while(T--){
    x=rd();y=rd();
    scanf("%s",s+1);n=strlen(s+1);
    int cnt1=0,cnt2=0;
    for(int i=1;i<=n;++i){
        if(x<0&&s[i]=='L'){
            cnt1++;
        }
        if(x>0&&s[i]=='R'){
            cnt1++;
        }
        if(y<0&&s[i]=='D'){
            cnt2++;
        }
        if(y>0&&s[i]=='U'){
            cnt2++;
        }
    }
    if(cnt1>=abs(x)&&cnt2>=abs(y))
        printf("YES\n");
    else printf("NO\n");
   }
   return 0;
}

B

垃圾模拟

#include <bits/stdc++.h>
#define N 1000009
using namespace std;
typedef long long ll;
int n,k;
int a[N];
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
int main(){
   int T=rd();
   while(T--){
    n=rd();k=rd();
    for(int i=1;i<=n;++i)a[i]=rd();
    int bo=0;int ans=0;
    for(int i=1;i<=k;++i){
        int bm=0;
        for(int j=1;j<n;++j){
            if(a[j]>=a[j+1])continue;
            else {bm=1;a[j]++;ans=j;break;}
        }
        if(!bm){bo=1;break;}
    }
    if(bo){printf("-1\n");}
    else printf("%d\n",ans);
   }
   return 0;
}

C

给定初始颜色序列和最终目标颜色序列,再给定\(m\)个人,每个人必须按顺序给某一个格子染上\(c_i\)的颜色,问是否可以染成目标序列。

我们可以将操作序列倒着考虑,如果能够一步染成目标序列那么优先染那个,否则我们可以考虑将其染到初始序列和目标序列颜色相同的位置上,或者我们可以染到之前已经被染过的地方(因为操作是可以覆盖的)。

当有一步不能染色或者最终没有染成目标序列时无解。

#include <bits/stdc++.h>
#define N 100009
using namespace std;
typedef long long ll;
set<int>vec[N];
int ans[N],a[N],b[N],c[N],rbs[N];
int n,m;
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
int main(){
   int T=rd();
   while(T--){
     n=rd();m=rd();
     for(int i=1;i<=n;++i)a[i]=rd();
     for(int i=1;i<=n;++i){
       b[i]=rd();
       if(b[i]!=a[i])vec[b[i]].insert(i);
       else rbs[b[i]]=i;
     }
     for(int i=1;i<=m;++i)c[i]=rd();
     int tg=0,bm=0;
     for(int i=m;i>=1;--i){
      if(!vec[c[i]].empty()){
        ans[i]=*vec[c[i]].begin();
        tg=ans[i];
        vec[c[i]].erase(vec[c[i]].begin());
      }
      else if(rbs[c[i]]){
        ans[i]=rbs[c[i]];tg=ans[i];
      }
      else if(tg){
        ans[i]=tg;
      }
      else bm=1;
     }
     for(int i=1;i<=n;++i)if(!vec[i].empty())bm=1;
     if(bm){printf("NO\n");}
     else{
      printf("YES\n");
       for(int i=1;i<=m;++i)printf("%d ",ans[i]);
      puts("");
     } 
     for(int i=1;i<=n;++i){
      vec[i].clear();rbs[i]=0;
     }
   }
   return 0;
}

D

题目大意:给定一张\(n\)个点的无向图,每条边上有一个字母\(a\)\(b\),问是否存在一条长度为\(m\)的回文路径,并且输出方案。

首先我们不难发现当存在点对\((i,j)\)使得来回的路径上的字母相同,那么我们一直来回地走这条边就好了,这样一定是回文的。

进一步的,我们还可以发现当\(m\)为奇数时,来回地走一条边同样也是回文的。

当这两种情况考虑过后,我们只剩下\(m\)为偶数且任意点对来回的字符都是相反的情况。

我们可以枚举\(i\)点,找到一组\((j,k)\)使得\((i,j)!=(i,k)\)这样我们可以在\((i,j)\)之间走\(m/2\)步,在\((i,k)\)之间走\(m/2\)步,因为对称性所以走出来的一定是回文串。

时间复杂度\(O(n^2)\)

#include <bits/stdc++.h>
#define N 1009
using namespace std;
typedef long long ll;
int n,m;
char s[N][N];
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
int main(){
   int T=rd();
   while(T--){
     n=rd();m=rd();
     for(int i=1;i<=n;++i){
      scanf("%s",s[i]+1);
     }
     int tg=0;
     for(int i=1;i<=n;++i){
       for(int j=1;j<i;++j){
         if(s[i][j]==s[j][i]){
           printf("YES\n");
           for(int k=1;k<=m+1;++k){
            if(k&1)printf("%d ",i);
            else printf("%d ",j);
           }
           puts("");
           tg=1;break;
         }
       }
       if(tg)break;
     }
      if(!tg&&(m&1)){
         printf("YES\n");
        for(int i=1;i<=m+1;++i){
          if(i&1)printf("1 ");
          else printf("2 ");
        }
        puts("");
        tg=1;
      }
      if(!tg){
        for(int i=1;i<=n;++i){
          int pre=0,tt=0;
          for(int j=1;j<=n;++j){
            if(i==j)continue;
            if(!pre)pre=s[i][j],tt=j;
            else if(s[i][j]!=pre){
               printf("YES\n");
              if((m/2)&1){
                for(int k=1;k<=m/2;++k)
                  if(k&1)printf("%d ",tt);
                  else printf("%d ",i);
                printf("%d ",i);
                for(int k=1;k<=m/2;++k)
                  if(k&1)printf("%d ",j);
                  else printf("%d ",i);
              }
              else{
                for(int k=1;k<=m/2;++k)
                  if(k&1)printf("%d ",i);
                  else printf("%d ",tt);
                printf("%d ",i);
                for(int k=1;k<=m/2;++k)
                  if(k&1)printf("%d ",j);
                  else printf("%d ",i);
              }
              puts("");
              tg=1;break;
            }
          }
          if(tg)break;
        }
      }
      if(!tg)printf("NO\n");
   }
   return 0;
}

E

题目大意:给定一个序列,每个位置的书都有一个颜色,每次操作可以将一本书放在最后,问将序列操作成相同颜色的书放在一起最少需要几步。

我们可以补集转化一下,问题变成求出不用移动的最大个数。

那么我们考虑两种不用移动的颜色,对于每一种颜色我们维护出最左和最右的端点,那么如果两种颜色都不用移动,那么它们一定不能相交。

于是变成了区间覆盖问题,用\(BIT\)查询即可。

但是这样会有一点问题,就是在右边一些位置是不用移动的,于是我们可以维护一下后缀最大值,在\(dp\)转移的时候更新答案(具体实现还是看代码比较好)。

#include <bits/stdc++.h>
#define N 500009
using namespace std;
typedef long long ll;
int n,m;
int l[N],r[N],tr[N],a[N],cnt[N],mxr[N],lim[N],num[N];
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
inline void add(int x,int y){
  while(x<=n)tr[x]=max(tr[x],y),x+=x&-x;
}
inline int query(int x){
  int ans=0;
  while(x)ans=max(ans,tr[x]),x-=x&-x;
  return ans;
}
int main(){
   n=rd();
   for(int i=1;i<=n;++i)a[i]=rd(),l[i]=n+1;
   int mx=0;
   for(int i=n;i>=1;--i){
    cnt[a[i]]++;
    l[a[i]]=min(l[a[i]],i);
    r[a[i]]=max(r[a[i]],i);
    mx=max(mx,cnt[a[i]]);
    mxr[i]=mx;
   }
   for(int i=1;i<=n;++i){
    num[r[i]]=cnt[i];
    lim[r[i]]=l[i];
   }
   for(int i=1;i<=n;++i)if(lim[i]){
    int nm=query(lim[i]-1)+num[i];
    mx=max(mx,nm+mxr[i+1]);
    add(i,nm);
   }
   cout<<n-mx;
   return 0;
}

F

一个奇妙构造。

posted @ 2021-02-06 10:05  comld  阅读(168)  评论(0编辑  收藏  举报