5.30日训练赛

A. Cthulhu

问是否有且仅有一个环,并且环的大小>=3个,要求图联通

直接DFS,如果存在一个环,那么重复访问的节点数目一定是2,首先考虑是链,那么DFS会到链的两个端点,那么由于这是一个环,两个端点会被另外一个端点访问,所以次数是2,最后

判断图是否联通即可。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
vector<int>G[1005];
int vis[105];
int cnt;
void dfs(int x,int fa){
    if (vis[x]!=0){
        vis[x]++;
       // cout<<"--"<<x<<endl;
        cnt++;
        return;
    }else {
        vis[x]=1;
    }
    for (int i=0;i<G[x].size();i++){
        if (G[x][i]!=fa)dfs(G[x][i],x);
    }
}
int main(){
  int n,m;
  while(~scanf("%d%d",&n,&m)){
      for (int i=1;i<=n;i++){
        G[i].clear();
      }
      cnt=0;
      int u,v;
      memset(vis,0,sizeof(vis));
      for (int i=1;i<=m;i++){
          scanf("%d%d",&u,&v);
          G[u].push_back(v);
          G[v].push_back(u);
      }
      dfs(1,-1);
      int flag=0;
      for (int i=1;i<=n;i++){
         if (vis[i]==0){
             flag=1;
         }
      }
      if (flag==0 && cnt==2){
        printf("FHTAGN!\n");
      }else {
        printf("NO\n");
      }
  }
  return 0;
}
View Code

B. Increasing by Modulo

给一串序列,每次操作可以给序列中任意多个数+1,并把这些数%M,问最少操作使得序列非递减。

二分最大操作,那么每个点的操作一定是小于或等于这个数,贪心的把前面的数尽量置成小的,维护一个前一个的最小状态,最后判断最大操作是否可行。从而判断得到二分上下界,得到最小答案

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxx = 300006;
int a[maxx];
int n,m;
bool judge(int x){
   int last=a[1];
   if (a[1]+x>=m)last=0;
   for (int i=2;i<=n;i++){
      int tmp=-1;
      if (a[i]>=last){
        tmp=a[i];
          if (a[i]+x>=m && (a[i]+x)%m>=last){
              tmp=last;
          }
      }else if (a[i]+x>=last){
           tmp=last;
      }
      last=tmp;
      if (tmp==-1)return 0;
   }
  return 1;
}
int main(){
   scanf("%d%d",&n,&m);
   for (int i=1;i<=n;i++){
      scanf("%d",&a[i]);
   }
   int l=0,r=m;
   int ans;
   while(l<=r){
      int mid=(l+r)/2;
      if (judge(mid)){
        ans=mid;
        r=mid-1;
      }else {
        l=mid+1;
      }
   }
   printf("%d\n",ans);
  return 0;
}
View Code

C. Tavas and Malekas

神仙题。。。给出一个模板串,以前原串长度,以前在原串中的和模板串匹配的多个首位置,问原串有多少种可能。

肯定是每次枚举原串首位置,外后更新,判断即可。但是很显然直接T飞。。。

首先如果原串两个相邻位置a[i]-a[i-1]>=len那么,a[i-1]不会影响a[i]随便放

如果a[i-1]-a[i-1]<len,那么我们考虑,模板串从剩下位置是从a[i]+len-a[i-1]位置和模板串的第一个位置开始匹配。。。如何快速判断这些位置是否匹配呢???

考虑Next数组,我们求出NEXT数组后,找失配位置,第一失配位置肯定是Len位置,每次把r指针置为Next[r],失配位置一定是r位置,打上标记,就能快速判断是否匹配。

最后数出没有标记的位置,就是可以随便放置的位置

/*
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define LL long long
using namespace std;
const int maxx = 1e6+7,MOD = 1e9+7;
char p[maxx];
int Next[maxx],a[maxx],n,m,vis1[maxx],vis[maxx];
LL qpow(LL a,LL b){
  LL sum=1;
  while(b){
     if(b&1)sum=sum*a%MOD;
     a=a*a%MOD;
     b/=2;
  }
  return sum;
}
void get_next(){
  memset(vis1,0,sizeof(vis1));
  Next[0]=-1;
  int k=-1,i=0;
  int len=strlen(p);
  while(i<len){
    if (k==-1 || p[i]==p[k]){
        i++;
        k++;
        Next[i]=k;
    }else
      k=Next[k];
  }
  int r=len;
  while(r!=-1){
     vis1[r]=1;
     r=Next[r];
  }
}
int main(){
  scanf("%d%d",&n,&m);
  scanf("%s",&p);

  for (int i=1;i<=m;i++){
    scanf("%d",&a[i]);
  }
  get_next();
  sort(a+1,a+1+m);
  int len=strlen(p);
  for (int i=1;i<=m;i++){
    if (i==1 || a[i]-a[i-1]>=len){
        for (int j=a[i];j<=a[i]+len-1;j++){
            vis[j]=1;
        }
    }else if (vis1[a[i-1]+len-a[i]]){
        for (int j=a[i-1]+len;j<=a[i]+len-1;j++){
            vis[j]=1;
        }
   }else {
      printf("0\n");
      return 0;
    }
  }
  LL ans;
  int cnt=0;
  for (int i=1;i<=n;i++){
    if (vis[i]==0){
        cnt++;
    }
  }
  ans=qpow((LL)26,cnt);
  printf("%lld\n",ans);
  return 0;
}
View Code

D. Fight Against Traffic

给出N个点,M条路,点i到点j的长度定义为i走个j经过的最小路径数目,给出起点和终点,在一些没有直接路径连接的点连接一条边,有多少对这样增加的路径,不影响i->j的路径长度

这题还是非常秀的。。。我们可以用BFS跑出从起点到每个点的距离,每个点到终点的距离,那么从起点到i的距离+终点到j的距离+1>=起点到终点的距离,起点到j+终点到i的距离+1>=起点到终点的距离,那对答案就是没有影响的。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxx = 1005;
vector<int>G[maxx];
int n,m;
bool vis[maxx];
int dis_s[maxx];
int dis_t[maxx];
int link[maxx][maxx];
void bfs(int dis[],int s){
   queue<int>q;
   memset(vis,0,sizeof(vis));
   vis[s]=1;
   q.push(s);
   dis[s]=0;
   while(q.size()){
      int x=q.front();
      q.pop();
      for (int i=0;i<G[x].size();i++){
        int nx=G[x][i];
        if (!vis[nx]){
            q.push(nx);
            vis[nx]=1;
            dis[nx]=dis[x]+1;
        }
      }
   }
}
int main(){
  int u,v,s,t;
  scanf("%d%d%d%d",&n,&m,&s,&t);
  while(m--){
     scanf("%d%d",&u,&v);
     G[u].push_back(v);
     G[v].push_back(u);
     link[u][v]=1;
     link[v][u]=1;
  }
  bfs(dis_s,s);
  bfs(dis_t,t);
  int cnt=0;
  for (int i=1;i<=n;i++){
    for (int j=i+1;j<=n;j++){
        if (!link[i][j] && dis_s[i]+1+dis_t[j]>=dis_s[t] && dis_t[i]+1+dis_s[j]>=dis_s[t]){
            cnt++;
        }
    }
  }
  printf("%d\n",cnt);
  return 0;
}
View Code

E.询问有多少对i,j满足a[i]<=y<=a[j],并且y%x==0中y个数是K的对数

其实就是一个式子a[j]/x-(a[i]-1)/x==k的个数

我们把a[i]排序,这不影响结果(自己脑补)。那么对于位置i,二分一个上下界满足这个式子的即可。。。

注意对于i位置的a[i],左边界应该是和a[i]相等的数的最小位置(因为有可能相等。。。)

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#define LL long long
using namespace std;
const int maxx = 1e5+6;
int n;
LL x,k;
LL a[maxx];
LL b[maxx];
map<int,int>p1;
int main(){
  while(~scanf("%d%lld%lld",&n,&x,&k)){
     p1.clear();
     for (int i=1;i<=n;i++){
         scanf("%lld",&a[i]);
     }
     sort(a+1,a+1+n);
     for (int i=1;i<=n;i++){
        if (a[i]!=a[i-1]){
            p1[a[i]]=i;
        }
     }
     LL ans=0;
     for (int i=1;i<=n;i++){
         int l=p1[a[i]],r=n;
         int low=n+1,up=-1;
         while(l<=r){
              int mid=(l+r)/2;
              if((a[mid]/x-(a[i]-1)/x)==k){
                up=max(up,mid);
              }
              if((a[mid]/x-(a[i]-1)/x)>k){
                   r=mid-1;
              }else {
                   l=mid+1;
              }
         }
         l=p1[a[i]],r=n;
         while(l<=r){
             int mid=(l+r)/2;
             if((a[mid]/x-(a[i]-1)/x)==k){
                low=min(low,mid);
              }
             if((a[mid]/x-(a[i]-1)/x)>=k){
                 r=mid-1;
             }else {
                 l=mid+1;
             }
         }
         if (up==-1 || low==n+1){
            continue;
         }
       //  cout<<up<<" "<<low<<endl;
         ans+=(up-low+1);
     }
    printf("%lld\n",ans);
  }
  return 0;
}
View Code

 F. Minimal string

字符串是坑啊。。。

给A一个字符串,你有两种操作,一种是把A字符串的最开始的放到字符串B的最后面,

把字符串B的最后面的字符放到字符串C的最后面

要求C的字典序最小,A,B到最后全为空,问C是多少?

写一个更新数组,从后往前遍历,意义为从i位置往后,最小的字典序的字母。

比较位置i的s[i]和更新数组,如果更新数组更优,把s[i]放到栈里面去,往后继续,否则把栈的比更新数组更优的字符放到字符串C中,然后继续往后找。直到最后

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stack>
using namespace std;
stack<char>sta;
char s[100005];
char best[100005];
int main(){
   while(~scanf("%s",s)){
     int len=strlen(s);
     best[len-1]=s[len-1];
     for (int i=len-2;i>=0;i--){
        best[i]=min(best[i+1],s[i]);
     }
     string ans;
     for (int i=0;i<len;i++){
         if (sta.size()==0){
            sta.push(s[i]);
         }else {

            while(sta.size() && sta.top()<=best[i]){
                ans+=sta.top();
                sta.pop();
            }
            sta.push(s[i]);
         }
     }
     while(sta.size()){
        ans+=sta.top();
        sta.pop();
     }
     cout<<ans<<endl;

   }
  return 0;
}
View Code

就做出A-E,貌似好丢脸。。。

posted @ 2019-05-31 21:35  bluefly-hrbust  阅读(161)  评论(0编辑  收藏  举报