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; }
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; }
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; }
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; }
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; }
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; }
就做出A-E,貌似好丢脸。。。