第二期4周周报

第四周:

第一题 A-B数对:

   这题怎么写呢??暴力肯定是不行的。本来以为要用双指针的策略求解,实则不然,这题就是之前做过某一题的弱化版,map存放每个数的个数,然后从1到最大值一直枚举B的情况,每次都让ans+=map[i]*map[i+c]就可以了。循环2^30看起来有点多,但是事实上也能过(悬),如果想要加快速率使用unordered_map要好,因为哈希表读取数据时常量复杂度,而map内部使用类似二叉树的存储结构需要O(logn)读取数据。

#include <bits/stdc++.h>
using namespace std;

int n, c;
map<int, int> cnt;
int ans, mmax; 
int main(void) 
{
	cin >> n >> c;
	vector<int> arr(n+1);
	for(int i = 1; i <= n; ++i)
	{
		cin >> arr[i];
		mmax = max(mmax, arr[i]);
		cnt[arr[i]]++;
	}
	int i = 1;
	while(i+c <= mmax)
	{
		ans += cnt[i]*cnt[i+c];
		++i;
	}
	cout << ans;
	return 0;
}

第二题 数位计算 

     大数模拟,事实上只需要对每一次的操作求余就可以了,本来想用python自带高精度数来模拟,但是事实上这样容易错,至于为什么就不明白了。雷区:每一次加,减,乘操作都需要进行取余,否则就会出现溢出或者结果不正确。这里主要用到前n项和公式,不能暴力的模拟要不然会TLE

//#include <bits/stdc++.h>
#define int long long
using namespace std;

const int MOD = 998244353;
signed main()
{
	int n;
	cin >> n;
	int sum = 0;
	for(int i = 1; i <= n; i*=10)
	{
		if(i <= n/10)
		{
			int a = (9*i)%MOD, b = (9*i+1)%MOD;
			sum = sum+(a*b/2)%MOD;
			sum %= MOD;
		}
		else
		{
			int a = (n-i+1)%MOD, b = (n-i+2)%MOD;
			sum = sum+(a*b/2)%MOD;
			sum %= MOD;
		}
	}
	cout << sum%MOD;
	return 0;
}

第三题 新国王游戏 

     主要用到贪心策略,但是不能瞎做。做贪心类题如果无从下手那么就考虑只有两个数的情况,再证明能够由此递推到更多数的情况即可。仅凭直觉时不一定可以的。然后数据量比较大为了避免卡输入输出可以用快读快写的板子。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1E6+6, mod = 1000000007;
struct person
{
	int l, r;
} p[maxn];
//像这种问题就考虑两个人的时候怎么划算
//2个人时假设1和2,1在前面,奖金就是b1*a2+b2
//1在后面就是b2*a1+b1
//前大于后,贪心算法这种题的求解就是要考虑两个的情况。
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n;
	cin >> n;
	for(int i = 0; i < n; ++i)
	{
		cin >> p[i].l >> p[i].r;
	}
	sort(p, p+n, [](const person &p1, const person &p2)
	{
		return p1.r*p2.l+p2.r > p2.r*p1.l+p1.r;
	});
	int sum = 0, mul = 1;
	for(int i = n-1; i >= 0; --i)
	{
		sum = sum+(mul*p[i].r)%mod;
		sum %= mod;
		mul *= p[i].l;
		mul %= mod;
	}
	cout << sum;
	return 0;
}

 第四题 完美数

 

    这题就比较难了,主要是对于排列组合问题的求解,但是可惜的是数据太大了,除法操作的取模是相当难的。这里利用求幂的方法来求逆元(离散数学没好好学的下场),逆元的求解方法为本元的MOD-2次方,至于原理我就不明白了。然后就是要用到快速幂的方法了。由于需要取模所以不能用到pow的方法。

#include <bits/stdc++.h>
#define int long long
using namespace std;
//目前有点bug
int a, b, m, ans;
const signed MOD = 1E9+7, N = 1E6+6;
int fac[N] = {1}, infac[N] = {1};
//可以用dp的办法来直接求阶乘
//An = n!, Amn = n!/m!,Cmn = n!/m!/(n-m)!
int qmi(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1) 
			res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
//dp求阶乘和反向阶乘
void dp()
{
	fac[0] = infac[0] = 1;
	for(int i = 1; i < N; ++i)		//乘以下
	{
		fac[i] = fac[i-1]*i%MOD;
	}
	infac[N-1] = qmi(fac[N-1], MOD-2);
	for(int i = N-2; i; --i)
	{
		infac[i] = infac[i+1]*(i+1)%MOD;
	}
}
int c(int a, int b)						//a比b大
{
	return fac[a]*infac[b]%MOD*infac[a-b]%MOD;
}
signed main()
{
	cin >> a >> b >> m;
	dp();
	for(int i = 0; i <= m; ++i)
	{
		int now = i*a+(m-i)*b, flag = 1;
		while(now)
		{
			int p = now%10;
			if(p != a && p != b)
			{
				flag = 0;
				break;
			}
			now /= 10;
		}
		if(flag)
		{
			ans = (ans+c(m, i))%MOD;
		}
	}
	cout << ans%MOD << endl;
	return 0;
}

 第五题 lusir的游戏

    这题主要涉及二分答案的策略,还有个坑点就是如果每次都获得能量值,那么能量值的增长速度将会很快,建筑物高度均为0的时候会指数倍增,即使开long long也不管用。策略就是能量值大于高度的最大范围就直接判取胜。(有时候大思路有了却败在小细节上面)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1E5+5;
int n, h[MAXN], ans = MAXN;
signed check(int now)
{
	for(int i = 1; i <= n; ++i)
	{
		if(h[i] > now)
		{
			now -= h[i]-now;
			if(now < 0)
			{
				return 0;
			}
		}
		else
		{
			now += now-h[i];		//能量值可能会指数倍增爆longlong
			if(now >= MAXN)			//因此如果now非常大了就直接win
			{
				return 1;
			}
		}
	}
	return 1;
}
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> h[i];
	}
	//妥妥的二分答案题
	int l = 0, r = MAXN, mid;
	while(l <= r)
	{
		mid = l+(r-l)/2;
		if(check(mid))
		{
			ans = min(ans, mid);		//往小的方向找
			r = mid-1;
		}
		else
		{
			l = mid+1;					//往大的方向找
		}
	}
	cout << ans << endl;
	return 0;
}

 第六题 BFS练习1

     正如这题,我本来以为直接用bfs暴力模拟是一种不好的策略,但是确实能做出来。原理就是bfs是广度优先遍历,所以已经搜索到的数据的深度<=没有搜索到的数据深度。因此可以用vis数组来存放数据是否已被访问到。vis[x]=1就没必要访问了,因为得不到更好的答案。同样的题目dfs却无法做到。

#include <bits/stdc++.h>
using namespace std;
int a, q;
const int MAX = 1E5+5;	//数据范围最大是MAX-1
int f[MAX], vis[MAX];
typedef pair<int, int> my;
//这题是树的存储结构,参数是起始点和数据规模
void bfs(int s)
{
	queue<my> q;		//一个队列
	q.push({s, 0});		//起始点
	while(!q.empty())
	{
		int n = q.front().first, d = q.front().second;
		q.pop();
		if(!vis[n])
		{
			vis[n] = 1;
			f[n] = d;
			if(n+1 < MAX && !vis[n+1])
			{
				q.push({n+1, d+1});
			}
			if(n-1 >= 0 && !vis[n-1])
			{
				q.push({n-1, d+1});
			}
			if(2*n < MAX && !vis[2*n])
			{
				q.push({2*n, d+1});
			}
			if(3*n < MAX && !vis[3*n])
			{
				q.push({3*n, d+1});
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> a >> q;
	bfs(a);
	int flag = 1;
	while(q--)
	{
		int n;
		cin >> n;
		if(flag)
		{
			flag = 0;
		}
		else
		{
			cout << ' ';
		}
		cout << f[n];
	}
	return 0;
}

 第七题 01序列2

 

     什么,这个题非常简单?emmm,其实还是排列组合问题。可以枚举1出现的个数。然后在剩下n-(i-1)*k个未被连续的0占满的位置中选择i个位置放置1,剩下的位置放置0即可。连续的0放到哪里没有影响。代码实现就是板子,但是比较难想。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 1E9+7, MX = 1E6+6;
int n, k;
int f[MX], inf[MX];
//传入的参数是MOD-2
int qmi(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b&1)
		{
			res = res*a%MOD;
		}
		a = a*a%MOD;
		b >>= 1;
	}
	return res;
}
void init()
{
	f[0] = inf[0] = 1;	//0和1的阶乘
	for(int i = 1; i < MX; ++i)
	{
		f[i] = f[i-1]*i%MOD;
	}
	inf[MX-1] = qmi(f[MX-1], MOD-2);
	for(int i = MX-2; i ; --i)
	{
		inf[i] = inf[i+1]*(i+1)%MOD;
	}
}
int C(int a, int b)		//a>b
{
	return f[a]*inf[b]%MOD*inf[a-b]%MOD;
}
signed main()
{
	cin >> n >> k;
	init();
	int ans = 1;
	for(int i = 1; i <= n-(i-1)*k; ++i)
	{
		ans = (ans+C(n-(i-1)*k, i))%MOD;
	}
	cout << ans << endl;
	return 0;
}

 第八题 整数光棍

    这题就比较适合用python来做了,直接秒杀。 

#python直接秒
x = eval(input())        #
b = n = 1
while (1):
    if (b % x == 0):
        print(int(b // x), n)
        break
    b = b*10+1
    n = n+1

第九题 碰撞2

     这就是纯模拟题了,遍历每一层(y高度)然后寻找能碰撞的点就可以了,可以用二维数组,也可以用一维数组+按y坐标排序。

#include <bits/stdc++.h>
using namespace std;

const int MX = 2E5+5;
struct person{
	int x, y;	//坐标
	int dir;
};
int n;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	vector<person> arr(n+1);
	for(int i = 1; i <= n; ++i)
	{
		cin >> arr[i].x >> arr[i].y;
	}
	string str;
	cin >> str;
	for(int i = 0; i < n; ++i)
	{
		arr[i+1].dir = (str[i]=='L'? -1:1);
	}
	sort(arr.begin()+1, arr.end(), [](const person& p1, const person& p2){
		return p1.y<p2.y;
	});	//定义排序规则
	int mmin = INT_MAX, mmax = INT_MIN, now = arr[1].y;
	for(int i = 1; i <= n; ++i)
	{
		if(arr[i].y != now)
		{
			if(mmin < mmax)
			{
				cout << "Yes" << endl;
				return 0;
			}
			mmin = INT_MAX;
			mmax = INT_MIN;
			now = arr[i].y;
		}
		if(arr[i].dir == 1)		//右走
		{
			mmin = min(mmin, arr[i].x);
		}
		else
		{
			mmax = max(mmax, arr[i].x);
		}
	}
	if(mmin < mmax)
	{
		cout << "Yes" << endl;
	}
	else
	{
		cout << "No" << endl;
	}
	return 0;
}

第十题 优美,最长上升子序列

     动态规划dp,但是需要时间复杂度O(nlogn),第一次做看错题了。每次找一个元素然后遍历他的下标倍数的元素就行。这样时间复杂度比O(n)快,也能符合要求。

#include <bits/stdc++.h>
using namespace std;
int t;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> t;
	while(t--)
	{
		int n, ans = 1;
		cin >> n;
		vector<int> arr(n+1), dp(n+1, 1);
		for(int i = 1; i <= n; ++i)
		{
			cin >> arr[i];
		}
		for(int i = 1; i <= n; ++i)
		{
			for(int j = 2*i; j <= n; j+=i)
			{
				if(arr[j] > arr[i])
				{
					dp[j] = max(dp[j], dp[i]+1);
					ans = max(ans, dp[j]);
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值