2017计蒜之道程序设计大赛初赛第五场题解
2017-05-22 17:48
627 查看
比赛链接
题目可以在比赛的题目列表中查看
命题人:镇高红
UCloud 刚刚建立一个新机房,近日正在进行网络搭建。机房内有 nnn 台服务器和 mmm 个分线器,整个机房只有一个网线出口。分线器的作用是将一根网线转换成多根网线。蒜头君也知道每个分线器输出的最大网线根数(不一定要将分线器输出的每根线都用上),问你至少需要使用多少个分线器才能使得每台服务器都有网线可用。
第一行输入 n,m(0≤n,m≤100)n,m(0 \le n,m \le 100)n,m(0≤n,m≤100)。
第二行输入包含 mmm 个整数的数组 A(0≤Ai≤10)A(0
\le A_i \le 10)A(0≤Ai≤10) 表示每个分线器输出的最大网线根数。
输出最少需要的分线器数量。若不能使得所有服务器都有网线可用,输出一行
一共需要 333 个分线器,最大输出根数分别为 7,3,27,3,27,3,2,连接方法如下图所示:
将分线器从大到小排序,优先用输出根数多的分线器。对于 非零 分线器集合 x_1,x_2,\ldots
x_mx1,x2,…xm,能接入的服务器总数为 \sum_{i=1}^{m}(x_i-1)+1∑i=1m(xi−1)+1。
注意处理分线器个数为 00、服务器个数为 00、分线器的输出根数为 00 的情况。
AC代码2:
贪心。
从大到小排序之后进行模拟,注意n=1和n=0的情况。
命题人:吴东学
每个 UCloud 用户会构造一个由数字序列组成的秘钥,用于对服务器进行各种操作。作为一家安全可信的云计算平台,秘钥的安全性至关重要。因此,UCloud
每年会对用户的秘钥进行安全性评估,具体的评估方法如下:
首先,定义两个由数字序列组成的秘钥 aaa 和 bbb近似匹配(≈\approx≈) 的关系。aaa 和 bbb 近似匹配当且仅当同时满足以下两个条件:
∣a∣=∣b∣|a|=|b|∣a∣=∣b∣,即aaa 串和 bbb 串长度相等。
对于每种数字 ccc,ccc 在 aaa 中出现的次数等于 ccc 在 bbb 中出现的次数。
此时,我们就称 aaa 和 bbb 近似匹配,即 a≈ba
\approx ba≈b。例如,(1,3,1,1,2)≈(2,1,3,1,1)(1,3,1,1,2)\approx(2,1,3,1,1)(1,3,1,1,2)≈(2,1,3,1,1)。
UCloud 每年会收集若干不安全秘钥,这些秘钥组成了不安全秘钥集合 TTT。对于一个秘钥sss 和集合 TTT 中的秘钥 ttt 来说,它们的相似值定义为:sss 的所有连续子串中与 ttt 近似匹配的个数。相似值越高,说明秘钥 sss 越不安全。对于不安全秘钥集合 TTT 中的每个秘钥 ttt,你需要输出它和秘钥sss 的相似值,用来对用户秘钥的安全性进行分析。
第一行包含一个正整数 nnn,表示sss 串的长度。
第二行包含 nnn 个正整数 s1,s2,...,sn(1≤si≤n)s_1,s_2,...,s_n(1\leq
s_i\leq n)s1,s2,...,sn(1≤si≤n),表示sss 串。
接下来一行包含一个正整数 mmm,表示询问的个数。
接下来 mmm 个部分:
每个部分第一行包含一个正整数 k(1≤k≤n)k(1\leq k\leq n)k(1≤k≤n),表示每个ttt 串的长度。
每个部分第二行包含 kkk 个正整数 t1,t2,...,tk(1≤ti≤n)t_1,t_2,...,t_k(1\leq
t_i\leq n)t1,t2,...,tk(1≤ti≤n),表示TTT 中的一个串 ttt。
输入数据保证 TTT 中所有串长度之和不超过 200000200000200000。
对于简单版本:1≤n,m≤1001\leq n,m\leq 1001≤n,m≤100;
对于中等版本:1≤n≤50000,1≤m≤a5001\leq n\leq 50000,1\leq m\leq 5001≤n≤50000,1≤m≤500;
对于困难版本:1≤n≤50000,1≤m≤1000001 \le n \le 50000, 1 \le m \le 1000001≤n≤50000,1≤m≤100000。
输出 mmm 行,每行一个整数,即与 TTT 中每个串 ttt 近似匹配的 sss 的子串数量。
对于第一个询问,(3,2,1,3)≈(2,3,1,3)(3,2,1,3)\approx(2,3,1,3)(3,2,1,3)≈(2,3,1,3),(3,2,1,3)≈(3,1,3,2)(3,2,1,3)\approx(3,1,3,2)(3,2,1,3)≈(3,1,3,2);
对于第二个询问,(1,3)≈(3,1)(1,3)\approx(3,1)(1,3)≈(3,1),(1,3)≈(1,3)(1,3)\approx(1,3)(1,3)≈(1,3);
对于第三个询问,(3,2)≈(2,3)(3,2)\approx(2,3)(3,2)≈(2,3),(3,2)≈(3,2)(3,2)\approx(3,2)(3,2)≈(3,2)。
对于一个长度为 lenlen 的询问,枚举 ss 的一个长度为 lenlen 的子串,然后暴力检验两个集合是否相同即可。
时间复杂度 O(n^2m)O(n2m)。
AC代码2:
暴力。
暴力枚举S串的每一个长度为m的子串,排序判断即可。
Java版:
考虑如何优化检验的复杂度,等价于判断两个可重集是否相同,给每个元素一个随机的 6464 位无符号整数权值,然后全部加起来作为集合的
Hash 值。那么一个子串的 Hash 值可以简单地由前缀和作差得到,每次检验的复杂度为O(1)O(1)。
对于一个长度为 lenlen 的询问,枚举 ss 的一个长度为 lenlen 的子串,然后检验两个集合是否相同即可。
时间复杂度 O(nm)O(nm)。
暴力。
$O(m*n)$的算法可以通过此题,每次询问$O(m)$扫S数组,统计不同数字的个数,每次移动最多只会变化两个数字,如果不同数字个数为$0$,那么答案加$1$。
考虑将询问按询问串长分组,那么最多只有 O(\sqrt{len})O(√len) 种长度。
对于每种长度 kk,将 ss 的所有长度为 kk 的子串的
Hash 值算出,与询问串 Hash 值一起排序,那么询问可以通过双指针实现。
时间复杂度 O(n\sqrt{len}\log
n)O(n√lenlogn)。
小数据打表,大数据暴力。
导致超时的主要原因是mm小的询问次数太多,可以把m≤10m≤10的答案直接暴力打表存起来,m>10m>10的用CC题的方法即可。
题目可以在比赛的题目列表中查看
UCloud 机房的网络搭建
命题人:镇高红UCloud 刚刚建立一个新机房,近日正在进行网络搭建。机房内有 nnn 台服务器和 mmm 个分线器,整个机房只有一个网线出口。分线器的作用是将一根网线转换成多根网线。蒜头君也知道每个分线器输出的最大网线根数(不一定要将分线器输出的每根线都用上),问你至少需要使用多少个分线器才能使得每台服务器都有网线可用。
输入格式
第一行输入 n,m(0≤n,m≤100)n,m(0 \le n,m \le 100)n,m(0≤n,m≤100)。第二行输入包含 mmm 个整数的数组 A(0≤Ai≤10)A(0
\le A_i \le 10)A(0≤Ai≤10) 表示每个分线器输出的最大网线根数。
输出格式
输出最少需要的分线器数量。若不能使得所有服务器都有网线可用,输出一行Impossible。
样例说明
一共需要 333 个分线器,最大输出根数分别为 7,3,27,3,27,3,2,连接方法如下图所示:样例输入
10 4 2 7 2 3
样例输出
3
将分线器从大到小排序,优先用输出根数多的分线器。对于 非零 分线器集合 x_1,x_2,\ldots
x_mx1,x2,…xm,能接入的服务器总数为 \sum_{i=1}^{m}(x_i-1)+1∑i=1m(xi−1)+1。
注意处理分线器个数为 00、服务器个数为 00、分线器的输出根数为 00 的情况。
#include <stdio.h> #include <bits/stdc++.h> #define mod 1000000007 typedef long long ll; using namespace std; int n,m; int s[105]; int main() { cin>>n>>m; for(int i=0;i<m;i++) cin>>s[i]; sort(s,s+m); int sum=1; if(sum>=n){ cout<<0; return 0; } for(int i=m-1;i>=0;i--){ sum=sum-1+s[i]; if(sum>=n){ cout<<m-i; return 0; } } cout<<"Impossible"; return 0; }
AC代码2:
贪心。
从大到小排序之后进行模拟,注意n=1和n=0的情况。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <cmath> using namespace std; int n,m; int a[100010]; bool cmp(int x,int y) { return x>y; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d",&a[i]); sort(a+1,a+1+m,cmp); int now = n; if(n==0||n==1) { printf("0\n"); return 0; } for(int i=1;i<=m;i++) { if(a[i] <= 0) break; if(a[i]<now) { now = now - a[i]; a[i+1]--; } else { now = 0; printf("%d\n",i); break; } } if(now!=0) { printf("Impossible\n"); } return 0; }
UCloud 的安全秘钥
命题人:吴东学每个 UCloud 用户会构造一个由数字序列组成的秘钥,用于对服务器进行各种操作。作为一家安全可信的云计算平台,秘钥的安全性至关重要。因此,UCloud
每年会对用户的秘钥进行安全性评估,具体的评估方法如下:
首先,定义两个由数字序列组成的秘钥 aaa 和 bbb近似匹配(≈\approx≈) 的关系。aaa 和 bbb 近似匹配当且仅当同时满足以下两个条件:
∣a∣=∣b∣|a|=|b|∣a∣=∣b∣,即aaa 串和 bbb 串长度相等。
对于每种数字 ccc,ccc 在 aaa 中出现的次数等于 ccc 在 bbb 中出现的次数。
此时,我们就称 aaa 和 bbb 近似匹配,即 a≈ba
\approx ba≈b。例如,(1,3,1,1,2)≈(2,1,3,1,1)(1,3,1,1,2)\approx(2,1,3,1,1)(1,3,1,1,2)≈(2,1,3,1,1)。
UCloud 每年会收集若干不安全秘钥,这些秘钥组成了不安全秘钥集合 TTT。对于一个秘钥sss 和集合 TTT 中的秘钥 ttt 来说,它们的相似值定义为:sss 的所有连续子串中与 ttt 近似匹配的个数。相似值越高,说明秘钥 sss 越不安全。对于不安全秘钥集合 TTT 中的每个秘钥 ttt,你需要输出它和秘钥sss 的相似值,用来对用户秘钥的安全性进行分析。
输入格式
第一行包含一个正整数 nnn,表示sss 串的长度。第二行包含 nnn 个正整数 s1,s2,...,sn(1≤si≤n)s_1,s_2,...,s_n(1\leq
s_i\leq n)s1,s2,...,sn(1≤si≤n),表示sss 串。
接下来一行包含一个正整数 mmm,表示询问的个数。
接下来 mmm 个部分:
每个部分第一行包含一个正整数 k(1≤k≤n)k(1\leq k\leq n)k(1≤k≤n),表示每个ttt 串的长度。
每个部分第二行包含 kkk 个正整数 t1,t2,...,tk(1≤ti≤n)t_1,t_2,...,t_k(1\leq
t_i\leq n)t1,t2,...,tk(1≤ti≤n),表示TTT 中的一个串 ttt。
输入数据保证 TTT 中所有串长度之和不超过 200000200000200000。
对于简单版本:1≤n,m≤1001\leq n,m\leq 1001≤n,m≤100;
对于中等版本:1≤n≤50000,1≤m≤a5001\leq n\leq 50000,1\leq m\leq 5001≤n≤50000,1≤m≤500;
对于困难版本:1≤n≤50000,1≤m≤1000001 \le n \le 50000, 1 \le m \le 1000001≤n≤50000,1≤m≤100000。
输出格式
输出 mmm 行,每行一个整数,即与 TTT 中每个串 ttt 近似匹配的 sss 的子串数量。
样例解释
对于第一个询问,(3,2,1,3)≈(2,3,1,3)(3,2,1,3)\approx(2,3,1,3)(3,2,1,3)≈(2,3,1,3),(3,2,1,3)≈(3,1,3,2)(3,2,1,3)\approx(3,1,3,2)(3,2,1,3)≈(3,1,3,2);对于第二个询问,(1,3)≈(3,1)(1,3)\approx(3,1)(1,3)≈(3,1),(1,3)≈(1,3)(1,3)\approx(1,3)(1,3)≈(1,3);
对于第三个询问,(3,2)≈(2,3)(3,2)\approx(2,3)(3,2)≈(2,3),(3,2)≈(3,2)(3,2)\approx(3,2)(3,2)≈(3,2)。
样例输入
5 2 3 1 3 2 3 4 3 2 1 3 2 1 3 2 3 2
样例输出
2 2 2
简单
对于一个长度为 lenlen 的询问,枚举 ss 的一个长度为 lenlen 的子串,然后暴力检验两个集合是否相同即可。时间复杂度 O(n^2m)O(n2m)。
#include <stdio.h> #include <bits/stdc++.h> #define mod 1000000007 typedef long long ll; using namespace std; int n,m; int s[50005],p[50005]; int flag[105]; int check(){ for(int i=0;i<102;i++) if(flag[i]!=0) return 0; return 1; } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>s[i]; cin>>m; while(m--){ int k; cin>>k; for(int i=1;i<=k;i++){ scanf("%d",p+i); flag[p[i]]++; flag[s[i]]--; } int ans=0; if(check()) ans++; for(int i=k+1;i<=n;i++){ flag[s[i]]--; flag[s[i-k]]++; if(check()) ans++; } memset(flag,0,sizeof(flag)); cout<<ans<<endl; } return 0; }
AC代码2:
暴力。
暴力枚举S串的每一个长度为m的子串,排序判断即可。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <cmath> using namespace std; int n,m; int s[100100]; int t[100100]; int p[100100]; map<int,int>m1,m2; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&s[i]); int Q; scanf("%d",&Q); while(Q--) { scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&t[i]); sort(t+1,t+1+m); if(m>n) { printf("0\n"); continue; } int ans = 0; for(int i=1;i<=n;i++) { int sz = 0; for(int j=i;j<=i+m-1;j++) sz++, p[sz] = s[j]; sort(p+1,p+1+m); bool suc = 1; for(int j=1;j<=m;j++) { if(p[j] == t[j]) continue; suc = 0; } ans = ans + suc; } printf("%d\n",ans); } return 0; }
Java版:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Collections; public class Two { public static void main(String[] args) throws IOException{ // TODO Auto-generated method stub BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String line=null; line=br.readLine(); int n=Integer.parseInt(line); String s=br.readLine(); String [] strs=s.split(" "); line=br.readLine(); int m=Integer.parseInt(line); while((m--)>0){ line=br.readLine(); int k=Integer.parseInt(line); String t=br.readLine(); String [] strst=t.split(" "); int ans=num(strs,strst); System.out.println(ans); } } public static int num(String[] a,String[] b){ if(a.length<b.length){ return 0; } else{ int la=a.length; int lb=b.length; int ans=0; for(int i=0;i+lb<=la;i++){ String [] temp=Arrays.copyOfRange(a, i, i+lb); if(similar(temp,b)){ ++ans; } } return ans; } } public static boolean similar(String[] a,String[] b){ if(a.length==b.length){ Arrays.sort(a); Arrays.sort(b); for(int i=0;i<a.length;++i){ if(!a[i].equals(b[i])){ return false; } } return true; } return false; } }
中等
考虑如何优化检验的复杂度,等价于判断两个可重集是否相同,给每个元素一个随机的 6464 位无符号整数权值,然后全部加起来作为集合的Hash 值。那么一个子串的 Hash 值可以简单地由前缀和作差得到,每次检验的复杂度为O(1)O(1)。
对于一个长度为 lenlen 的询问,枚举 ss 的一个长度为 lenlen 的子串,然后检验两个集合是否相同即可。
时间复杂度 O(nm)O(nm)。
#include <stdio.h> #include <bits/stdc++.h> #define mod 1000000007 typedef long long ll; using namespace std; int n,m; int s[50005],p[50005]; int flag[50005],vis[50005]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>s[i]; cin>>m; while(m--){ int k; cin>>k; int sum=0,ss=0; int ans=0; for(int i=1;i<=k;i++){ scanf("%d",p+i); flag[p[i]]=1; if(vis[p[i]]==0){ sum++; } vis[p[i]]++; } for(int i=1;i<=k;i++){ if(flag[s[i]]==1){ vis[s[i]]--; if(vis[s[i]]==0) ss++; if(vis[s[i]]==-1) ss--; } } if(ss==sum) ans++; // cout<<ss<<endl; for(int i=k+1;i<=n;i++){ if(flag[s[i]]==1){ vis[s[i]]--; if(vis[s[i]]==0) ss++; if(vis[s[i]]==-1) ss--; } if(flag[s[i-k]]==1){ vis[s[i-k]]++; if(vis[s[i-k]]==0) ss++; if(vis[s[i-k]]==1) ss--; } // cout<<ss<<endl; if(ss==sum) ans++; } memset(flag,0,sizeof(flag)); memset(vis,0,sizeof(vis)); cout<<ans<<endl; } return 0; }AC代码2
暴力。
$O(m*n)$的算法可以通过此题,每次询问$O(m)$扫S数组,统计不同数字的个数,每次移动最多只会变化两个数字,如果不同数字个数为$0$,那么答案加$1$。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <cmath> using namespace std; int n,m; int s[100010]; int t[100010]; int m1[100010]; int m2[100010]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&s[i]); int Q; scanf("%d",&Q); while(Q--) { scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&t[i]); if(m>n) { printf("0\n"); continue; } memset(m1,0,sizeof m1); memset(m2,0,sizeof m2); for(int i=1;i<=m;i++) m1[s[i]]++, m2[t[i]]++; int bu = 0; for(int i=1;i<=n;i++) if(m1[i]!=m2[i]) bu++; int ans = 0; if(bu == 0) ans++; for(int i=m+1;i<=n;i++) { int pre = i-m; int now = i; if(m1[s[pre]] == m2[s[pre]]) bu++; else if(m1[s[pre]]-1 == m2[s[pre]]) bu--; m1[s[pre]]--; if(m1[s[now]] == m2[s[now]]) bu++; else if(m1[s[now]]+1 == m2[s[now]]) bu--; m1[s[now]]++; if(bu == 0) ans++; } printf("%d\n",ans); } return 0; }
困难
考虑将询问按询问串长分组,那么最多只有 O(\sqrt{len})O(√len) 种长度。对于每种长度 kk,将 ss 的所有长度为 kk 的子串的
Hash 值算出,与询问串 Hash 值一起排序,那么询问可以通过双指针实现。
时间复杂度 O(n\sqrt{len}\log
n)O(n√lenlogn)。
小数据打表,大数据暴力。
导致超时的主要原因是mm小的询问次数太多,可以把m≤10m≤10的答案直接暴力打表存起来,m>10m>10的用CC题的方法即可。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <cmath> using namespace std; int n,m; int s[100010]; int t[100010]; int m1[100010]; int m2[100010]; map<vector<int>,int>ans; void work() { vector<int>vv; for(int i=1;i<=m;i++) { vv.push_back(t[i]); } sort(vv.begin(),vv.end()); printf("%d\n",ans[vv]); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&s[i]); for(int i=1;i<=n;i++) { vector<int>vv; for(int j=i;j<=i+10-1;j++) { vv.push_back(s[j]); sort(vv.begin(),vv.end()); ans[vv]++; } } int Q; scanf("%d",&Q); while(Q--) { scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&t[i]); if(m>n) { printf("0\n"); continue; } if(m<=10) { work(); continue; } memset(m1,0,sizeof m1); memset(m2,0,sizeof m2); for(int i=1;i<=m;i++) m1[s[i]]++, m2[t[i]]++; int bu = 0; for(int i=1;i<=n;i++) if(m1[i]!=m2[i]) bu++; int ans = 0; if(bu == 0) ans++; for(int i=m+1;i<=n;i++) { int pre = i-m; int now = i; if(m1[s[pre]] == m2[s[pre]]) bu++; else if(m1[s[pre]]-1 == m2[s[pre]]) bu--; m1[s[pre]]--; if(m1[s[now]] == m2[s[now]]) bu++; else if(m1[s[now]]+1 == m2[s[now]]) bu--; m1[s[now]]++; if(bu == 0) ans++; } printf("%d\n",ans); } return 0; }
相关文章推荐
- 2017计蒜之道程序设计大赛初赛第五场
- 2017计蒜之道程序设计大赛初赛第五场
- 2017计蒜之道程序设计大赛初赛第三场题解
- 2017计蒜之道程序设计大赛初赛第六场题解
- 2017计蒜之道程序设计大赛初赛第一场题解
- 2017计蒜之道程序设计大赛初赛第四场题解
- 2017计蒜之道程序设计大赛初赛第二场题解
- 2017计蒜之道程序设计大赛复赛题解
- 计蒜之道 2017 程序设计大赛 - 计蒜客 复赛 D 百度地图导航 最短路、Dijkstra的拓展
- “中兴捧月”杯 校园赛事嘉年华 | 程序设计大赛(初赛) | 数字化婚姻尝试配对
- 计蒜之道 2017 程序设计大赛 - 计蒜客 复赛 F 腾讯消消乐 状态压缩dp、枚举+剪枝
- star.baidu.com程序设计大赛初赛、复赛题目
- 计蒜之道 2017 程序设计大赛 - 计蒜客 复赛 B Windows 画图 几何、平面、枚举
- AIX 程序设计大赛---AIX正方形问题
- 2008程序设计大赛
- AIX 程序设计大赛---AIX正方形问题
- 程序设计大赛—页面置换算法(LRU)
- 程序设计大赛--约瑟夫问题
- 程序设计大赛-留下的学生
- AIX 程序设计大赛---AIX正方形问题