A. UCloud 机房的网络搭建
UCloud 刚刚建立一个新机房,近日正在进行网络搭建。机房内有 n 台服务器和 m 个分线器,整个机房只有一个网线出口。分线器的作用是将一根网线转换成多根网线。蒜头君也知道每个分线器输出的最大网线根数(不一定要将分线器输出的每根线都用上),问你至少需要使用多少个分线器才能使得每台服务器都有网线可用。
输入格式
第一行输入 n,m(0≤n,m≤100)。
第二行输入包含 m 个整数的数组 A(0≤Ai≤10) 表示每个分线器输出的最大网线根数。
输出格式
输出最少需要的分线器数量。若不能使得所有服务器都有网线可用,输出一行Impossible
。
样例说明
一共需要 3 个分线器,最大输出根数分别为 7,3,2,连接方法如下图所示:
样例输入
10 4 2 7 2 3
样例输出
3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <math.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100000+7;
int cmp(int x,int y)
{
return x>y;
}
int a[N];
int main()
{
int n, m;
scanf("%d %d", &n, &m);
for(int i=1;i<=m;i++) scanf("%d", &a[i]);
sort(a+1,a+m+1,cmp);
if(n==1||n==0)
{
cout<<0<<endl;
return 0;
}
int cnt=-1;
for(int i=1;i<=m;i++)
{
if(a[i]<=0) break;
if(a[i]>=n)
{
cnt=i;
break;
}
n-=a[i];
a[i+1]-=1;
}
if(cnt==-1) printf("Impossible\n");
else cout<<cnt<<endl;
return 0;
}
B. UCloud 的安全秘钥(简单)
C. UCloud 的安全秘钥(中等)
每个 UCloud 用户会构造一个由数字序列组成的秘钥,用于对服务器进行各种操作。作为一家安全可信的云计算平台,秘钥的安全性至关重要。因此,UCloud 每年会对用户的秘钥进行安全性评估,具体的评估方法如下:
首先,定义两个由数字序列组成的秘钥 a 和 b 近似匹配(≈) 的关系。a 和 b 近似匹配当且仅当同时满足以下两个条件:
- ∣a∣=∣b∣,即 a 串和 b 串长度相等。
- 对于每种数字 c,c 在 a 中出现的次数等于 c 在 b 中出现的次数。
此时,我们就称 a 和 b 近似匹配,即 a≈b。例如,(1,3,1,1,2)≈(2,1,3,1,1)。
UCloud 每年会收集若干不安全秘钥,这些秘钥组成了不安全秘钥集合 T。对于一个秘钥 s 和集合 T 中的秘钥 t 来说,它们的相似值定义为:s 的所有连续子串中与 t 近似匹配的个数。相似值越高,说明秘钥 s 越不安全。对于不安全秘钥集合 T 中的每个秘钥 t,你需要输出它和秘钥 s 的相似值,用来对用户秘钥的安全性进行分析。
输入格式
第一行包含一个正整数 n,表示 s 串的长度。
第二行包含 n 个正整数 s1,s2,...,sn(1≤si≤n),表示 s 串。
接下来一行包含一个正整数 m,表示询问的个数。
接下来 m 个部分:
每个部分第一行包含一个正整数 k(1≤k≤n),表示每个 t 串的长度。
每个部分第二行包含 k 个正整数 t1,t2,...,tk(1≤ti≤n),表示 T 中的一个串 t。
输入数据保证 T 中所有串长度之和不超过 200000。
对于简单版本:1≤n,m≤100;
对于中等版本:1≤n≤50000,1≤m≤500;
对于困难版本:1≤n≤50000,1≤m≤100000。
输出格式
输出 m 行,每行一个整数,即与 T 中每个串 t 近似匹配的 s 的子串数量。
样例解释
对于第一个询问,(3,2,1,3)≈(2,3,1,3),(3,2,1,3)≈(3,1,3,2);
对于第二个询问,(1,3)≈(3,1),(1,3)≈(1,3);
对于第三个询问,(3,2)≈(2,3),(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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <math.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100000+7;
int a[N], b[N], vis1[N], vis2[N];
int judge(int k)
{
for(int i=1;i<=k;i++)
{
if(vis1[b[i]]!=vis2[b[i]]) return 0;
}
return 1;
}
int main()
{
int n, m;
scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%d", &a[i]);
scanf("%d", &m);
while(m--)
{
int k;
scanf("%d", &k);
for(int i=1;i<=k;i++) scanf("%d", &b[i]);
memset(vis1,0,sizeof(vis1));
memset(vis2,0,sizeof(vis2));
for(int i=1;i<=k;i++)
{
vis1[a[i]]++,vis2[b[i]]++;
}
int cnt=judge(k);
for(int i=k+1;i<=n;i++)
{
vis1[a[i-k]]--,vis1[a[i]]++;
cnt+=judge(k);
}
cout<<cnt<<endl;
}
return 0;
}
困难版本 哈希离线处理+随机化标记
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
typedef pair<int,LL> pi;
const int N = 1e5+17;
vector<int>p;
vector<pi>dict[N];
LL op[N],b[N];
int a[N], cnt[N], res[N];
int main()
{
for(int i=1;i<=N-10;i++) op[i]=(LL)rand()*rand();//使每一个数字都有唯一标识且重复率低
int n, t, k;
scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%d", &a[i]);
scanf("%d", &t);
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=t;i++)
{
scanf("%d", &k);
if(!cnt[k]) p.push_back(k),cnt[k]++;//记录出现过的区间长度,避免重复计算
LL now=0;
for(int j=1;j<=k;j++)
{
int x;
scanf("%d", &x);
now+=op[x];
}
dict[k].push_back(make_pair(i,now));//离线处理时需要的下标
}
for(int i=0;i<p.size();i++)
{
k=p[i];
LL now=0;
for(int j=1;j<=k;j++) now+=op[a[j]];
int num=0;
b[num++]=now;
for(int j=k+1;j<=n;j++)//O(n)遍历
{
b[num]=b[num-1]+op[a[j]]-op[a[j-k]];
num++;
}
sort(b,b+num);
for(int j=0;j<dict[k].size();j++)
res[dict[k][j].first]=upper_bound(b,b+num,dict[k][j].second)-lower_bound(b,b+num,dict[k][j].second);//利用二分快速计算出答案
}
for(int i=1;i<=t;i++)
printf("%d\n",res[i]);
return 0;
}