牛客挑战赛 46
B
题目链接:
乍一看还以为是Pollard_rho算法,其实大可不必。
发现n小于\(1e18\),我们可以将n分为两部分(分块思想降低时间复杂度)。
剔除小于等于\(4000\)的所有质因子,剩余的设为x,设此时得到的答案为\(minnum\)
如果x为\(1\),那我们得到答案,可以直接返回
否则知道x的质因子一定大于\(4000\),可以分类讨论
若x可写成\(x=p^{(1/4)}\),\(minnum = min(minnum,4)\),返回
若x可写成\(x=p^{(1/3)}\),\(minnum = min(minnum,3)\),返回
否则x若可写成平方的形式,\(minnum = min(minnum,2)\),注意要区分为4的情况
否则我们知道\(minnum\)一定为\(1\)
时间复杂度大约为\(O(t) * 1000\)
(将大问题化为小问题然后分类讨论)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 10001;
int prime[N], flag[N], tot, t;
LL n;
LL mulfou(LL x) { return x*x*x*x; }
LL multhe(LL x) { return x*x*x; }
void get_prime(int n)
{
for(int i = 2;i <= n; i++)
{
if(!flag[i]) prime[++ tot] = i;
for(int j = 1;j <= tot && i * prime[j] <= n; j++)
{
flag[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
void work(LL n)
{
if(n == 1) { printf("0\n");return; }
int minnum = 100; LL x = n;
for(int i = 1;i <= tot; i++)
{
if(x % (LL) prime[i] != 0) continue;
int res = 0;
while(x % prime[i] == 0) ++ res,x /= prime[i];
minnum = min(minnum,res);
}
if(x == 1) {printf("%d\n",minnum);return;}
if(minnum == 1) {printf("1\n");return;}
LL sqr = pow(x,1.0/4.0), sqr2 = pow(x,1.0/2.0);
if(mulfou(sqr) == x || mulfou(sqr + 1) == x || mulfou(sqr - 1) == x)
{ minnum = min(minnum,4);printf("%d\n",minnum); return; }
else if(sqr2 * sqr2 == x) { minnum = min(minnum,2);printf("%d\n",minnum);return; }
else
{
LL sqr3 = (LL)pow(1.0*x,1.0/3.0);
if(multhe(sqr3) == x || multhe(sqr3 + 1) == x || multhe(sqr3 - 1) == x)
{ minnum = min(3,minnum);printf("%d\n",minnum); return; }
else { printf("1\n"); return; }
}
}
int main()
{
get_prime(4000);
scanf("%d",&t);
while(t --)
{
scanf("%lld",&n);
work(n);
}
}
/*
1
512384096008
*/
C
题目链接:
排列
考虑\(dp\),我们思考如何设计状态
将第i个数插入i-1个数中,我们考虑会新增多少个超级逆序对
假设将\(i\)插入后\(i\)的位置为\(l\),\(i-1\)的原来的位置为\(l2\)
如果\(l2>=l\) 我们会新产生\(i-l-1\)个逆序对
否则\(l2<l\) 我们会新产生\(i-l\)个逆序对
设j为逆序对的个数,我们可以得到如下的状态转移方程
\(dp[i][j][l]+=dp[i-1][j-i+l+1][l2]\)如果\(l2>=l\)
否则\(dp[i][j][l]+=dp[i-1][j-i+l][l2]\)如果\(l2<l\)
容易发现\(l2\)是线性的,我们可以通过前缀和优化降低时间复杂度
同时可通过设计\(las\)数组舍弃\(i\),降低空间复杂度
总时间复杂度为\(O(n^3)\),空间复杂度为\(O(n^2)\)
代码:
#include <bits/stdc++.h>
#define mod 998244353
#define LL long long
using namespace std;
int n, k;
const int N = 511;
LL dp[N][N], sum[N][N], las[N][N];
LL ksm(LL a,LL b)
{
LL res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod; b >>= 1;
}
return res;
}
int main()
{
scanf("%d%d",&n,&k);
dp[0][1] = 1;las[0][1] = 1;
for(int i = 2;i <= n; i++)
{
memset(dp,0,sizeof(dp));
for(int j = 0;j <= k; j++)
for(int l = 1;l <= i; l++)
sum[j][l] = sum[j][l - 1] + las[j][l];
for(int j = 0;j <= k; j++)
for(int l = 1;l <= i; l++)
{
if(j - i + l + 1 >= 0) dp[j][l] = (dp[j][l] + sum[j - i + l + 1][i] - sum[j - i + l + 1][l - 1]) % mod;
if(j - i + l >= 0) dp[j][l] = (dp[j][l] + sum[j - i + l][l - 1]) % mod;
}
for(int j = 0;j <= k; j++)
for(int l = 1;l <= n; l++)
las[j][l] = dp[j][l];
}
LL ans = 0;
for(int i = 1;i <= n; i++) ans = (ans + dp[k][i]) % mod;
printf("%lld\n",ksm(ans,mod - 2));
}
D
题目链接:
数列
查询有多少\([l,r]\)区间满足每个数出现\(k\)的倍数次
即为\(1\)到\(r\)与\(1\)到\(l-1\)每个数相减的次数为\(k\)的倍数次
可以使用哈希维护
记录每个数出现的次数为\(cnt[x]\),哈希值为\(hash[x]\)
那么前缀哈希和即为\(\sum_{x} cnt[x]*hash[x]\)(\(cnt\)注意要模\(k\))
当我们循环到i时候,更新哈希值,查询得到的哈希值在之前map[hash]出现的次数
(\([l,r]\)区间的哈希值为0)
就可以得到以\(i\)结尾的符合条件区间的个数,从而得到答案
#include <bits/stdc++.h>
#define LL long long
#define ull unsigned long long
using namespace std;
const int N = 1e6 + 101;
int n, k;
int a[N];
ull hash[N], cnt[N], now;
map<ull,int>mp;
ull rnd() { return (ull)rand() * (ull) rand() * (ull) rand(); }
signed main()
{
srand(time(0));
scanf("%d%d",&n,&k);
for(int i = 1;i <= n; i++) hash[i] = rnd();
for(int i = 1;i <= n; i++) scanf("%d",&a[i]);
mp[0] ++;LL ans = 0;
for(int i = 1;i <= n; i++)
{
int x = a[i];
now -= cnt[x] * hash[x];
cnt[x] = (cnt[x] + 1) % k;
now += cnt[x] * hash[x];
ans += mp[now];
mp[now] ++;
}
cout << ans << endl;
}
本文来自博客园,作者:zjz2333,转载请注明原文链接:https://www.cnblogs.com/zjz2333/p/14139241.html