Week 3 DAY 5:

Game on Ranges - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Game on Ranges

这道题题目意思有点难理解,加上是英文提面,这里解释一下,有两个人A和B,

一开始给定一个n,是1到n的排列,A人负责选取区间,B负责在该区间选一个数删掉,操作n次,也就把区间里的数全部都删掉了。题目给出了A每次选择的区间,要求B人,在A给出的区间中,B删去了哪些点

讲解一下其中一个样例(样例三)

输入样例:
6
1 1
3 5
4 4
3 6
4 5
1 6

输出样例:(这道题输出顺叙不做要求)

1 1 1
3 5 3
4 4 4
3 6 6
4 5 5
1 6 2

给定的是一个长度为6的数组

1.A:选过1 1的区间,那么此时B一定只能选取数字1给他删掉

2.接着A:选了3  5的区间,B要删掉一个数,这里B可以删掉3 4 5,此时不确定 B要删几,继续往下;

3.A选择了4 4的区间,同样的B只能删掉数字4;

4.A选择了数字 3 6 的区间,B可以选择 3 5 6,为什么没有4,因为第3步已经选走了。

5.A选择了4 5 区间,因为4 被选走了,B只能选择5;

6 A选择了1 6 区间,此时B能选2 3 6;、

我们倒回去看那些B步确定删掉的第2步,第4步,和第六步;

第二步在看只能删掉3,第四步只能删掉6,第六步只能删掉2;

所以根据A给出的区间;

就可以的代输出样例;

1 1 1

4 4 4

4 5 5

3 5 3

3 6 6

1 6 2

那么我们就可以先删掉区间最短的那一个组合,因为他们可以选择的数字是最少得;

所以我们可以写一个结构体数组对区间长度进行排序;依次确定每个区间B会删掉什么数字;

标记,保证每个区间删掉的数字不相同;

AC:

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct nod{
	int r;
	int l;
	int d;
}a[1005];
int vis[1005];

bool cmp(nod x,nod y){
	return x.d<y.d;
}

signed main(){
	int t;
	cin>>t;
	while(t--){
		memset(vis,0,sizeof(vis));
		memset(a,0,sizeof(a));
		int n;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i].l>>a[i].r;
			a[i].d=a[i].r-a[i].l+1;
		}
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++){
	//	cout<<endl;
		//cout<<"i: "<<i<<" "<<a[i].l<<" "<<a[i].r<<endl;;
			if(a[i].l==a[i].r){
				vis[a[i].l]=1;
				cout<<a[i].l<<" "<<a[i].r<<" "<<a[i].l<<endl;
			}
			else {
				for(int j=a[i].l;j<=a[i].r;j++){
					if(vis[j]==0){
						vis[j]=1;
						cout<<a[i].l<<" "<<a[i].r<<" "<<j<<endl;
						break;
					}
				}
			}
		}
        cout<<endl;
	}
	return 0;
}

[ABC225F] String Cards - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

扩展:

C++ fill()函数最详细介绍-CSDN博客

D - Bouquet (atcoder.jp)

Bouquet

这道题就是一道排列组合的问题;

每次遇到模就给我整不会了;

这道题很简单,就是有n朵花,给你两个数a,b,要求花数不能为a b,那么就是一个简单的组合问题;总的方案数就是n朵里面选1朵,或者选两朵。。。。选n朵,所以不排除a和b的那两种方案数就Cn1+Cn2+Cn3+.....Cnn=2的n次方-Cn0=2^n-1;

然后我们在针对不排除a和b的方案数中减去Cna和Cnb的排列组合;

就是这个模数这里,需要用到卢卡斯定理卢卡斯定理 - OI Wiki (oi-wiki.org)

Lucas 定理用于求解大组合数取模的问题,其中模数必须为素数。正常的组合数运算可以通过递推公式求解(详见 排列组合),但当问题规模很大,而模数是一个不大的质数的时候,就不能简单地通过递推求解来得到答案,需要用到 Lucas 定理。

记住模版就好

long long Lucas(long long n, long long m, long long p) {
  if (m == 0) return 1;
  return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;
}

这里算组合数有很多种方法,详细见:【数论】求组合数的四种方法_c++求组合数-CSDN博客

这里用到的是时间复杂度为NlogN的预处理法;

先预处理出范围内所有数的阶乘和阶乘逆元,根据第一个公式直接取需要的阶乘和阶乘逆元的值即可。

1.什么是逆元:就是数论里的倒数乘法逆元通俗易懂的理解方法-CSDN博客

取模运算不满足除法的分配率;

即(a/b)%p != (a%p/b%p)%p;

此时就需要乘法的逆元

 举个例子

2 x 3 ≡ 1 (mod 5 ) 即 2 x 3 对 5 取模和 1 对 5 取模是同余的 都是1 。 其中 3就是 2的乘法逆元
实战:
在这里插入图片描述

乘法的逆元就是将一个除法写成了乘法。

求逆元的几种方式:【数论】求逆元的四种方法-CSDN博客

2.这里求逆元的阶乘用到了费马小定理;

,x是a的最小逆元

定理内容:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)

变换一下可得

所以求组合数的代码就是

int C(int a, int b){    // 求组合数C(a, b)
	int res=1;
	for(int i=1,j=a;i<=b;i++,j--){
		res=res*j%mod;
		res=res*km(i,mod-2,mod)%mod;
	}
	return res;
}

然后再套一个快速幂的模版:

int km(int a, int n, int m)
{
	int ans = 1;
	while(n){
		if(n&1){
			ans = (ans * a) % m;
		}
		a = (a * a) % m;
		n >>= 1;
	}
	return ans;
}

最后AC:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,b;
const int mod=1000000007;
int km(int a, int n, int m)
{
	int ans = 1;
	while(n){
		if(n&1){
			ans = (ans * a) % m;
		}
		a = (a * a) % m;
		n >>= 1;
	}
	return ans;
}
int C(int a, int b){    // 求组合数C(a, b)
	int res=1;
	for(int i=1,j=a;i<=b;i++,j--){
		res=res*j%mod;
		res=res*km(i,mod-2,mod)%mod;
	}
	return res;
}
int lucas(int a, int b)
{
	if (a < mod && b < mod) return C(a, b);
	return (int)C(a % mod, b % mod)* lucas(a /mod, b /mod) %mod;
}
signed main(){
	int n;
	cin>>n;
	cin>>a>>b;
	int ans=km(2,n,mod);
	ans-=1;
	ans-=lucas(n,a)%mod+lucas(n,b)%mod;
	cout<<(ans+mod)%mod<<endl;
	return 0;
}

String Cards

[ABC225F] String Cards - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题的第一感觉就是对字符串进行排序然后直接拼接;

但是举一个例子,比如:

b ba这两个字符的最小字典树拼接法就是bab

但是按照排序后拼接就是bba;因为b的字典序比ba小

所以这么做显然是存在问题的;

这里需要用到动态数组

在 n 个字符串中选取 k 个任意连接,得到字典序最小的字符串。
按照 A+B > B+A 的顺序排序。
设 dpi,j表示前 i 个字符串取 j 个得到的最小字符。

dpi+1,j = dpi,j
dpi+1,j=min(dpi+1,j,s+dpi,j−1),s 表 示 可 选 择 的 字 符 串 

#include<bits/stdc++.h>
using namespace std;
string s[100], dp[55];
bool cmp(string a, string b){
	return a+b > b+a;
}
int main() {	
	int n, k;
	cin >> n >> k;
	for(int i = 1; i <= n; i++) cin >> s[i];
	sort(s+1, s+n+1, cmp);
	for(int i = 1; i <= n; i++) dp[i] = "{";
	dp[0] = "";
	for(int i = 1; i <= n; i++){
		for(int j = k-1; j >= 0; j--){
			dp[j+1] = min(dp[j+1], s[i] + dp[j]);
		}
	}
	cout << dp[k] << endl;
	return 0;
}

 Make Equal With Mod

Problem - B - Codeforces

这道题有三种情况可以输出YES;

第一种是全为1,;

第二种是1没有出现过一次;

第三种是有1存在,那么我们就需要把非1的数除余非1数-1,使他除余后为1;

但是如果存在连续的两个数,除余后必有一个为0,所以就不能输出YES;

所以当有1存在的时候需要保证时候有连续的两个数出现;

#include<bits/stdc++.h>
using namespace std;
#define int long long
int b[200005];
signed main(){
		int t;
		cin>>t;
		while(t--){
			//set<int>a;
		int f=0;
		int p=0;
		int n;
		cin>>n;
		for(int i=1;i<=n;i++){
			int x;
			cin>>b[i];
			x=b[i];
			if(x==1){
				f++;
			}
		}
		sort(b+1,b+1+n);
		for(int i=2;i<=n;i++){
			if(b[i]-b[i-1]==1){
				p=1;
				break;
			}
		}
		if(f==n||f==0||p==0)cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
		}
	return 0;
}

Buy an Integer

[ABC146C] Buy an Integer - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

解释一下这里的d(N)是什么意思,是十进制下N有几位,

二分求出答案即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,b,k;
bool check(int x){
	int sum=0;
	int num=x;
	while(num){
		num/=10;
		sum++;
	}
	if(a*x+b*sum<=k)return 1;
	else return 0;
}
signed main(){

	cin>>a>>b>>k;
	int l=1,r=1e9;
	while(l<r){
		int mid=(l+r+1)/2;
		if(check(mid)){
			l=mid;
		}
		else r=mid-1;
	}
	
	if(check(l))cout<<l<<endl;
	else cout<<0<<endl;
	
}

String Formation

[ABC158D] String Formation - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题唯一的难点就是翻转那里超时,其实不用真的翻转如果翻转次数为计数,只用把放前面的操作放后面,放后面的操作放前面就可以了。每次翻转都累加翻转次数就能出答案

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
string s;
deque<char>ans;
signed main(){
cin>>s;
int q;
for(int i=0;i<s.size();i++){
	ans.push_back(s[i]);
}
cin>>q;
int fan=0;
while(q--){
	int op;
	cin>>op;
	if(op==1){
		fan++;
	}
	else {
		int n;
		char x;
		cin>>n;
		cin>>x;
	//	cout<<fan<<endl;
		if(n==1){
			if(fan%2!=0){
				ans.push_back(x);
			}
			else ans.push_front(x);
		}
		else {
			if(fan%2!=0){
				ans.push_front(x);
			}
			else ans.push_back(x);
		}
	}
}
if(fan%2!=0)reverse(ans.begin(),ans.end());
for(auto p:ans){
	cout<<p;
}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值