CSUST--4.11排位周赛第八场 (全解)
作者:互联网
emmm,怎么基本都爆零了啊,B题是个签到题啊。。。诶,你们这次应该感觉到了社会的毒打了,是不是很难受,是不是很绝望QWQ
题目说明:
A.这真是一个水题吗(Hash+二分)
B.动漫明星大乱斗(签到题-思维)
C.欺负萌新的佳爷(二分)
D.小明的数学作业(DP+思维)
E.还没想好题目的题(最短路)
比赛链接:http://acm.csust.edu.cn/contest/88
比赛过后无法提交,请到problem中提交
A.这真是一个水题吗
题目大意:给你一个字符串$s_{1}$和字符串$s_{2}$,$s_{2}$作为匹配串,问允许最大不匹配数为k时的最多匹配位置有多少($s_{1},s_{2}<=1e6$)
Sample Input
adada add 1
Sample Output
2
emmm,看起来暴力好像能过,然后白给了一发,我想多了,假设$s_{1}$长度为1e6,$s_{2}$的长度为1e3。。。然后极限复杂度就是1e9,爆了
不过我们可以先看看暴力的代码:
#include <bits/stdc++.h> using namespace std; const int mac=1e6+10; char s1[mac],s2[mac]; int main() { int k; scanf ("%s%s",s1,s2); scanf ("%d",&k); int ans=0; int len1=strlen(s1),len2=strlen(s2); for (int i=0; i<len1; i++){ if (i+len2-1>=len1) break; int dif=0; for (int j=0; j<len2; j++){ if (s1[i+j]!=s2[j]) dif++; if (dif>k) break; } if (dif<=k) ans++; } printf("%d\n",ans); return 0; }View Code
我们先将这两个串hash化,对于有多少个不匹配点,我们可以直接从1到p进行枚举,每枚举一次就对初始匹配点跳跃一次,在枚举的时候我们二分在当前初始匹配点的时候最远能匹配的长度。。。。然后似乎就没了QAQ
以下是AC代码:
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int mac=1e6+10; const ull mod=53; char s1[mac],s2[mac]; ull hash1[mac],hash2[mac],miss[mac]; int p; void get_Hash(char *s1,char *s2) { int len1=strlen(s1+1),len2=strlen(s2+1); for (int i=1; i<=len1; i++) hash1[i]=hash1[i-1]*mod+(s1[i]-'a'+1); for (int i=1; i<=len2; i++) hash2[i]=hash2[i-1]*mod+(s2[i]-'a'+1); } int check(int x,int y,int mid) { ull t1=hash1[x+mid-1]-hash1[x-1]*miss[mid]; ull t2=hash2[y+mid-1]-hash2[y-1]*miss[mid]; if (t1==t2) return 1; return 0; } int find(int x,int y,int len2) { int l=1,r=len2-y+1; int ans=0; while (l<=r){ int mid=(l+r)>>1; if (check(x,y,mid)) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } int ok(int pos,int len2) { int x=pos,y=1; for (int i=1; i<=p; i++){ int use=find(x,y,len2); x=x+use+1,y=y+use+1; if (y>len2) return 1; } int use=find(x,y,len2); y+=use; if (y>len2) return 1; return 0; } int main() { scanf ("%s%s%d",s1+1,s2+1,&p); miss[0]=1; for (int i=1; i<mac; i++) miss[i]=miss[i-1]*mod; get_Hash(s1,s2); int len1=strlen(s1+1),len2=strlen(s2+1); int ans=0; for (int i=1; i<=len1-len2+1; i++) if (ok(i,len2)) ans++; printf ("%d\n",ans); return 0; }View Code
B.动漫明星大乱斗
题目大意:给你第1-n个人要挑战的人,这n个人当中有一个发起挑战的人,自挑战发起开始每次被选的人都会选一个人与之对决,并不停的进行下去。问无论发起者是谁,都最多打一场的人有多少,并输出他们的编号(从小到大)
Sample Input
2 2 1
Sample Output
0
实际上我们画个图就出来了,这是个有向图,当最多只有一场的时候那么他们的度只能小于等于1比如:
B的度为2,那么答案只能是A和C,所以这题就很简单了,记录一下每个点的出度和入度就完事了
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=1e5+10; int a[mac],du_in[mac],du_out[mac]; int id[mac]; int main() { int n; scanf ("%d",&n); for (int i=1; i<=n; i++){ scanf ("%d",&a[i]); du_out[i]++; du_in[a[i]]++; } int cnt=0; for (int i=1; i<=n; i++) if (du_in[i]+du_out[i]<=1) id[++cnt]=i; sort(id+1,id+1+cnt); printf("%d\n",cnt ); for (int i=1; i<=cnt; i++) printf("%d ",id[i]); printf("\n"); return 0; }View Code
C.欺负萌新的佳爷
题目大意:给你一个n*m的矩阵,矩阵中第$x$行,第$y$列数字的大小为$ax^2+bx+cy$,问你第$k$大的数字是多少。给出$n,m,k(n,m<=1e5),a,b,c([-1e5,1e5])$
Sample Input
3 3 4 1 1 1
Sample Output
9
样例构成的矩阵为:
3 4 5
7 8 9
13 14 15
刚开始看的时候可能有点儿蒙,但其实也不是很难,我们观察他所给定的式子就会发现如果固定下$x$那么该行的值是单调的,也就是说我们可以快速地求出每一行的第$k$大,这有什么用呢?事实上,整体的第$k$大也就是代表着矩阵中有$k-1$个数比他大,那么我们枚举答案$ans$,看看是否有$k-1$个数比他大,但我们得到的$ans$是一个区间即[第k大,第k+1大),那么我们只需要取最小值就OK了。而枚举答案实际上就是二分答案,最后的总复杂度就是$O(nlogval)$(val<=2e18)
以下是AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll inf=1e18; ll n,m,a,b,c,k; ll ok(ll x) {//计算有多少个大于x的数 ll num=0; if (c==0){ for (int i=1; i<=n; i++){ ll p=a*i*i+b*i; if (p>x) num+=m; } } else if (c<0){//每行递减 for (int i=1; i<=n; i++){ ll p=a*i*i+b*i; ll y=(x-p)/c;//计算x所处的位置 if (y<=0) continue; else if (y>m) num+=m; else if (abs(x-p)%abs(c)) num+=y; else num+=y-1; } } else { for (int i=1; i<=n; i++){ ll p=a*i*i+b*i; ll y=(x-p)/c; if (y<=0) num+=m; else if (y>m) continue; else if ((x-p)%c) num+=m-y; else num+=m-y; } } return num; } int main() { cin>>n>>m>>k; cin>>a>>b>>c; ll l=inf,r=-inf,mid,ans=0; for (int i=1; i<=n; i++){ if (c>0){ l=min(l,a*i*i+b*i+c); r=max(r,a*i*i+b*i+c*m); } else { l=min(l,a*i*i+b*i+c*m); r=max(r,a*i*i+b*i+c); } } while (l<=r){ mid=(l+r)>>1; ll nb=ok(mid); if (nb<k){ ans=mid; r=mid-1; } else { l=mid+1; } } cout<<ans<<endl; return 0; }View Code
D.小明的数学作业
题目大意:给你一个数列,让你用里面的数字组成一个最长的等差数列,问这个最长的等差数列多长?
Sample Input
6 0 1 3 5 6 9
Sample Output
4
emmmm,首先我们能够想到的是如果等差数列的其中两项被固定了,那么这个等差数列就是固定的,所以我们可以枚举等差数列的其中两项来得到一个公差,并通过当前第二项的$a_i+d=val$判断val是否存在,并且存在哪里,我们就可以通过这个地方来进行转移,即:$dp[i][j]=dp[j][a[j]+d]+1$当然,我们的$a[j]+d$肯定是用map来映射的一个该值的位置。我们还要进行一下小优化,如果公差太大的话就直接跳出循环:$if (1LL*d*ans>a[n]-a[1]) break;$
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=5e3+10; short dp[mac][mac]; int a[mac]; int main() { int n; scanf("%d", &n); for (int i=1; i<=n; i++) scanf ("%d",&a[i]); sort(a+1,a+1+n); unordered_map<int,short>q,q1; short ans=2; for (int i=1; i<=n; i++) q[a[i]]=i,q1[a[i]]++,ans=max(ans,q1[a[i]]);//记录a[i]出现的位置 for (int j=n; j>=2; j--){//枚举两项 for (int i=j-1; i>=1; i--){ int d=a[j]-a[i]; if (1LL*d*ans>a[n]-a[1]) break;//公差太大了 int last=a[j]+d; if (!q.count(last)) dp[i][j]=2; else dp[i][j]=dp[j][q[last]]+1; ans=max(ans,dp[i][j]); } } printf("%d\n", (int)ans); }View Code
F.还没想好题目的题
题目大意:给你一个图让你求$1\rightarrow 2\rightarrow 3\rightarrow \cdots \rightarrow n$的距离
Sample Input
1 3 2 1 2 1 2 3 2
Sample Output
3
题目的数据范围不是很大,n是1000,m是10000,所以我们可以直接跑n遍dij最短路就完事儿了。。。然后你会发现T了。。。dij是算边跑的,它的理论复杂度应该是$O(mlogm)$。。。所以我们要压缩一下时间,其实我们只需要当dij将点扩展到st+1的时候就完事了,后面的点不用管它。这样就大大节约了时间
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=1e4+10; typedef long long ll; const ll inf=1e18+10; ll dis[mac]; struct node { int to,next,w; }eg[mac<<1]; int head[mac],num=0; bool vis[mac]; struct pt { int id; ll s; bool operator<(const pt&a)const{ return s>a.s; } }; void dij(int st,int n) { for (int i=1; i<=n; i++) dis[i]=inf; memset(vis,false,sizeof vis); dis[st]=0; priority_queue<pt>q; q.push(pt{st,0}); while (!q.empty()){ pt now=q.top(); q.pop(); int u=now.id; if (u==st+1) break; if (vis[u]) continue; vis[u]=true; for (int i=head[u]; i!=-1; i=eg[i].next){ int v=eg[i].to; if (vis[v]) continue; if (dis[u]+eg[i].w<dis[v]){ dis[v]=dis[u]+eg[i].w; q.push(pt{v,dis[v]}); } } } } void add(int u,int v,int w) { eg[++num]=node{v,head[u],w}; head[u]=num; } int main() { int t,n,m; scanf ("%d",&t); while (t--){ scanf ("%d%d",&n,&m); memset(head,-1,sizeof head); num=0; for (int i=1; i<=m; i++){ int u,v,w; scanf ("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); } ll ans=0; for (int i=1; i<n; i++){ dij(i,n); ans+=dis[i+1]; } printf("%lld\n",ans); } return 0; }View Code
标签:4.11,周赛,int,ll,CSUST,else,Sample,mac,ans 来源: https://www.cnblogs.com/lonely-wind-/p/12690805.html