牛客挑战赛 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;
}
posted @ 2020-12-15 16:04  zjz2333  阅读(92)  评论(0编辑  收藏  举报