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;
}