集美大学第十一届校程序设计竞赛(同步赛)
C.因子数小于等于4的个数
题目
多组,且没有“保证总数不超过。。。”,算下来只能是O(1)查找了。
如果一个一个筛因数,1e6*sqrt(1e6) 就会T掉,那当时就不知道怎么写了。最后发现了一个好的找因数的方法:
我们在外面创建两个数组。从1开始预处理我们可能要判断的数字。因数对应的就是倍数,我们第二维循环每次加j,就可以找i的倍数,也就是j的因数,就可以找出每个数字自身的因数个数。
我赛时想了很久。所以写,写题解也可以不让自己看手机,可能也是好处。
点击查看代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int a[1000005];
int b[1000005];
void is_(int n){
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){
a[j]++;
}
}
for(int i=1;i<=n;i++){
b[i]=b[i-1]+(a[i]<=4);
}
}
void sovle()
{
int l,r;
cin>>l>>r;
cout<<b[r]-b[l-1]<<endl;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
is_(1000000);
cin>>t;
while (t--)
{
sovle();
}
return 0;
}
K. 数字三角形
这个题赛事没想出来,我自己给自己下班了,还是不行……
一个数可以分解成多位 2^n 的和。利用这个性质来解决题目。
点击查看代码
#include<bits/stdc++.h>
#define endl '\n'
// #define int __int128
#define int long long
using namespace std;
const int N = 1e5+10;
const int inf=1e18;
#define debug cout << "!! !!debug!! !!" << endl;
const int MAXN = 30005;
int n,m;
int pw[27];
void sovle(){
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
int mx= *max_element(a.begin()+1,a.begin()+n+1);
int e=1;
int cnt=0;
while(e<=mx){
e<<=1;
cnt++;
}//找出最大数字有多少二进制位。
e>>=1;
cnt--;//减去一位,因为最后要和其他位相加。
pw[0]=1;
for(int i=1;i<=26;i++){
pw[i]=pw[i-1]*2;//把这些二进制位全都表示出来。
}
int len=cnt+1;//循环从0开始,len设为位数加一
vector ans(cnt+2,vector<int>());//二维变长数组。
for(int i=0;i<=len;i++){
ans[i].resize(i+1,0);//填充大小
ans[i][0]=0;//设置初始值
for(int j=1;j<=i;j++){
if(j&1){
ans[i][j]=1;//如果当前是奇数位,就决定填充数字。
}else{
ans[i][j]=0;//偶数就填0
}
}
}
cout<<cnt+2<<endl;
//第一行只有一个数字,那个数字必须要是0。
//而2^n无法通过相加得到。只能再列一行。预备最大数就是2^n的情况。
for(int i=0;i<len+1;i++){
for(int j=0;j<ans[i].size();j++){
if(j>0){
cout<<" ";
}if(ans[i][j]){
cout<<pw[i-1];//每行都拿2^(i-1)赋值。
}else{
cout<<0;
}
}
cout<<endl;
}
vector<string>res;
for(int i=1;i<=n;i++){
int pos=0;
int y=0;
int val=a[i];
string now;
for(int j=1;j<=len;j++){
if(val>>(j-1)&1){//判断2进制位情况。
if(ans[j][y]){
now+='L';//如果这一位是1并且当前位置有等效2的平方数的话,那么就直接下降,因为不是正规三角形
//(目前now的情况还停留在上一行!)
}else{
now+='R';//当前这一位不存在所需2的平方数,那么这一位相邻的后一位一定是。
y++;//y++来改变当前位置。
}
}else{//反之,则与上面相反就好了。
if(!ans[j][y]){
now+='L';
}else{
now+='R';
y++;
}
}
}
res.push_back(now);
}
for(auto&s:res){
cout<<s<<endl;
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t = 1;
// cin>>t;
while (t --){
sovle();
}
return 0;
}
F. 串
题目
字符串哈希
append函数是向string的后面追加字符或字符串。
首先将这个字符串在后面拷贝一份,将长度变成
。
然后字符串哈希,于是我们寻找子串 [i,i+n) 的第 k 小的字符串即可。
那么如何快速比较两个字符串的大小呢?
· 因为有哈希值这个东西,考虑最长公共前缀 LCP
· 我们去二分 LCP 的长度,每次比较哈希值,
· 然后找到第一个不同的位置,
· 直接比较大小即可
这样我们就可以在 O(log n) 的时间内,比较两个字符串的大小。
点击查看代码
#include<bits/stdc++.h>
// #include<ext/pb_ds/assoc_container.hpp>
#define endl '\n'
// #define int __int128
#define int long long
using namespace std;
const int N = 2e6+10;
const int inf=1e18;
#define debug cout << "!! !!debug!! !!" << endl;
const int MAXN = 30005;
typedef unsigned long long ull;
const int P = 131;
const int mod=998244353;
ull h[N], p[N];
int n,k;
string s;
inline ull find(int l, int r) {
if(l>r) return 0;
return h[r] - h[l - 1] * p[r - l + 1];
}
int id[N];
void sovle(){
cin>>n>>k;
cin>>s;
s.append(s);
//向后复制一下字符串。
//因为要满足题目循环字符串条件。
s=" "+s;
p[0]=1;
//预处理哈希值。
for(int i=1;i<=2*n;i++){
h[i]=(h[i-1]*P+s[i]);
p[i]=(p[i-1]*P);
id[i]=i;
}
//二分排列字符子串大小。
sort(id+1,id+n+1,[&](int i,int j){
int l=1,r=n;
int ans=n;
while(l<=r){
int mid=(l+r)/2;
if(find(i,i+mid-1)!=find(j,j+mid-1)){
ans=mid;
r=mid-1;
}else{
l=mid+1;
}
}
if(ans==n){
return i<j;
}
return s[i+ans-1]<=s[j+ans-1];
});
//找到第K个。
cout<<s.substr(id[k],n)<<endl;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t = 1;
cin>>t;
while (t --){
sovle();
}
return 0;
}

浙公网安备 33010602011771号