1.区间嵌套
https://www.acwing.com/problem/content/description/5462/
2.卡片
3.逆序对https://www.luogu.com.cn/problem/P1908
4.合唱队形
https://www.luogu.com.cn/problem/P1091
5.回文日期
6.既约分数
7.数的分解
8.九宫幻方
区间嵌套https://www.acwing.com/problem/content/description/5462/
给定 n� 个正整数区间,编号 1∼n1∼�。
其中,第 i� 个区间为 [li,ri][��,��]。
请你找到一对不同的整数 j,k�,�(1≤j,k≤n1≤�,�≤�),使得区间 j� 完全包含于区间 k�。
如果 lj≥lk��≥�� 且 rj≤rk��≤��,则区间 j� 完全包含于区间 k�。
输入格式
第一行包含整数 n�。
接下来 n� 行,其中第 i� 行包含两个整数 li,ri��,��。
输出格式
如果题目无解,则输出一行
-1 -1
。否则,在一行内输出一对不同的整数 j,k�,�,满足区间 j� 完全包含于区间 k�。
如果答案不唯一,则输出任意合理答案均可。
数据范围
前 66 个测试点满足 1≤n≤51≤�≤5。
所有测试点满足 1≤n≤3×1051≤�≤3×105,1≤li≤ri≤1091≤��≤��≤109。
输入样例1:
5 1 10 2 9 3 9 2 3 2 9
输出样例1:
2 1
输入样例2:
3 1 5 2 6 6 20
输出样例2:
-1 -1
思路:按照左区间从大到小排序,然后比较相邻节点的右区间大小
cpp
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
#define INF 0x3f3f3f3f
const int N=3e5+5;
struct node{
int l;
int r;
int idx;
};
node e[N];
bool cmp(const node&a ,const node &b){
if (a.l==b.l) return a.r>b.r;
else return a.l<b.l;
}
signed main(){
int n;
cin>>n;
for (int i=1;i<=n;++i){
cin>>e[i].l>>e[i].r;
e[i].idx=i;
}
sort(e+1,e+1+n,cmp);
for (int i=1;i<n;++i){
if (e[i].r>=e[i+1].r){
cout<<e[i+1].idx<<" "<<e[i].idx;
return 0;
}
}
cout<<-1<<" "<<-1;
return 0;
}
卡片
小蓝有很多数字卡片,每张卡片上都是数字 00 到 99。
小蓝准备用这些卡片来拼一些数,他想从 11 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从 11 拼到多少。
例如,当小蓝有 3030 张卡片,其中 00 到 99 各 33 张,则小蓝可以拼出 11 到 1010,
但是拼 1111 时卡片 11 已经只有一张了,不够拼出 1111。
现在小蓝手里有 00 到 99 的卡片各 20212021 张,共 2021020210 张,请问小蓝可以从 11 拼到多少?
思路:暴力模拟
cpp
#include <iostream>
using namespace std;
int a[10];
void cc(int b){
while (b>0){
a[b%10]++;
b/=10;
}
}
bool check(){
for (int i=0;i<10;++i){
if (a[i]>2021)return true;
}
return false;
}
int main()
{
for (int i=1;i<=4400;++i){
cc(i);
if (check()){
cout<<i-1;
return 0;
}
}
return 0;
}
逆序对https://www.luogu.com.cn/problem/P1908
题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为"逆序对"的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ��>��ai>aj 且 �<�i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
输入格式
第一行,一个数 �n,表示序列中有 �n个数。
第二行 �n 个数,表示给定的序列。序列中每个数字不超过 109109。
输出格式
输出序列中逆序对的数目。
输入输出样例
输入 #1复制
6
5 4 2 6 3 1
输出 #1复制
11
说明/提示
对于 25%25% 的数据,�≤2500n≤2500
对于 50%50% 的数据,�≤4×104n≤4×104。
对于所有数据,�≤5×105n≤5×105
思路:树状数组+离散化
cpp
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
#define INF 0x3f3f3f3f
const int N=5e5+5;
int n,Rank[N],tree[N];
struct node{
int val;
int num;
}a[N];
bool cmp(const node& a, const node& b){
if (a.val==b.val) return a.num<b.num;
return a.val<b.val;
}
void update(int x,int c){
while (x<=N){
tree[x]+=c;
x+=lowbit(x);
}
}
int query(int x){
int res=0;
while (x>0){
res+=tree[x];
x-=lowbit(x);
}
return res;
}
signed main(){
cin>>n;
for (int i=1;i<=n;++i){
cin>>a[i].val;
a[i].num=i;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;++i){
Rank[a[i].num]=i;
}
int sum=0;
for (int i=n;i>0;--i){
update(Rank[i],1);
sum+=query(Rank[i]-1);
}
cout<<sum;
}
合唱队形https://www.luogu.com.cn/problem/P1091
题目描述
�n 位同学站成一排,音乐老师要请其中的 �−�n−k 位同学出列,使得剩下的 �k 位同学排成合唱队形。
合唱队形是指这样的一种队形:设 �k 位同学从左到右依次编号为 1,2,1,2, ... ,�,k,他们的身高分别为 �1,�2,t1,t2, ... ,��,tk,则他们的身高满足 �1<⋯<��>��+1>t1<⋯<ti>ti+1> ... >��(1≤�≤�)>tk(1≤i≤k)。
你的任务是,已知所有 �n 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式
共二行。
第一行是一个整数 �n(2≤�≤1002≤n≤100),表示同学的总数。
第二行有 �n 个整数,用空格分隔,第 �i 个整数 ��ti(130≤��≤230130≤ti≤230)是第 �i 位同学的身高(厘米)。
输出格式
一个整数,最少需要几位同学出列。
输入输出样例
输入 #1复制
8
186 186 150 200 160 130 197 220
输出 #1复制
4
说明/提示
对于 50%50% 的数据,保证有 �≤20n≤20。
对于全部的数据,保证有 �≤100n≤100。
思路:动态规划,找最长单增子串,从左往右和从右往左都要遍历,然后重新遍历数组最高点,找到结果
cpp
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
#define INF 0x3f3f3f3f
int dp1[105],dp2[105];
int n,a[105];
signed main(){
cin>>n;
for (int i=1;i<=n;++i) cin>>a[i];
for (int i=1;i<=n;++i){
dp1[i]=1;
dp2[i]=1;
}
for (int i=1;i<=n;++i){
for (int j=1;j<i;++j){
if (a[j]<a[i]) dp1[i]=max(dp1[i],dp1[j]+1);
}
}
for (int i=n;i>=1;--i){
for (int j=n;j>i;--j){
if (a[j]<a[i]) dp2[i]=max(dp2[i],dp2[j]+1);
}
}
int res=0;
for (int i=1;i<=n;++i){
res=max(res,(dp1[i]+dp2[i]-1));
}
cout<<n-res;
}
回文日期
2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 "yyyymmdd" 的格式写成一个 8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。
有人表示 20200202 是 "千年一遇" 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。
也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。对此小明也不认同,因为大约 100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 2121 年 12 月 12 日。算不上 "千年一遇",顶多算 "千年两遇"。
给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。
输入描述
输入包含一个八位整数 �N,表示日期。
对于所有评测用例,10000101≤�≤8999123110000101≤N≤89991231,保证 �N 是一个合法日期的 8 位数表示。
输出描述
输出两行,每行 1 个八位数。第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。
cpp
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
#define INF 0x3f3f3f3f
int days[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};
string check(int date){
string s=to_string(date),t=to_string(date);
reverse(t.begin(),t.end());
s+=t;
int y=stoi(s.substr(0,4)),m=stoi(s.substr(4,2)),d=stoi(s.substr(6,2));
if (y%400==0 || (y%4==0 && y%100!=0)) days[2]=29;
else days[2]=28;
if (m<1 || m>12 || d>days[m] || d<1) return "-1";
return s;
}
string check2(int date){
string s=to_string(date),t=to_string(date);
reverse(t.begin(),t.end());
s+=t;
if (s[0]==s[2] && s[1]==s[3]) return s;
return "-1";
}
signed main(){
int date;
cin>>date;
string ans=" ";
for (int i=date/10000;;++i){
if (check(i)=="-1" || check(i)==to_string(date)) continue;
if (ans==" ") ans=check(i);
if (check2(i)!="-1"){
cout<<ans<<endl<<check2(i);
return 0;
}
}
}
既约分数
如果一个分数的分子和分母的最大公约数是 11,这个分数称为既约分数。
例如 34,18,7143,81,17, 都是既约分数。
请问,有多少个既约分数,分子和分母都是 11 到 20202020 之间的整数(包括 11 和 20202020)?
cpp
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
#define INF 0x3f3f3f3f
int gcd(int a,int b){
if (b==0) return a;
return gcd(b,a%b);
}
signed main(){
int cnt=0;
for (int i=1;i<=2020;++i){
for (int j=1;j<=2020;++j){
if (gcd(i,j)==1) cnt++;
}
}
cout<<cnt;
}
数的分解
把 20192019 分解成 33 个各不相同的正整数之和,并且要求每个正整数都不包含数字 22 和 44,一共有多少种不同的分解方法?
注意交换 33 个整数的顺序被视为同一种方法,例如 1000+1001+181000+1001+18 和 1001+1000+181001+1000+18 被视为同一种。
cpp
#include <iostream>
using namespace std;
bool hefa(int a){
while (a>0){
int m=a%10;
a/=10;
if (m==2 || m==4) return false;
}
return true;
}
int main()
{
int cnt=0;
for(int i=1;i<=2019;++i){
for (int j=i+1;j<2019-i-j;++j){
if (hefa(i) && hefa(j) && hefa(2019-i-j)){
cnt++;
}
}
}
cout<<cnt;
return 0;
}
九宫幻方
小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将 1~9 不重复的填入一个 3*3 的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。
三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀:"二四为肩,六八为足,左三右七,戴九履一,五居其中",通过这样的一句口诀就能够非常完美的构造出一个九宫格来。
4 9 2
3 5 7
8 1 6
有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干镜像和旋转操作之后得到。现在小明准备将一个三阶幻方(不一定是上图中的那个)中的一些数抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一个解。
而你呢,也被小明交付了同样的任务,但是不同的是,你需要写一个程序。
输入描述
输入仅包含单组测试数据。
每组测试数据为一个 3*3 的矩阵,其中为 0 的部分表示被小明抹去的部分。
给出的矩阵至少能还原出一组可行的三阶幻方。
输出描述
如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出"Too Many"(不包含引号)。
思路:DFS找空格内的字符,然后判断是否可以
cpp
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
#define INF 0x3f3f3f3f
int n=0;
int a[10][10],ans[10][10],vis[10],cnt=0;
pair<int,int>p[10];
bool check(){
int p=a[1][1]+a[3][3]+a[2][2];
for (int i=1;i<=3;++i){
int m=0,c=0;
for (int j=1;j<=3;++j){
m+=a[i][j];
c+=a[j][i];
}
if (m!=p|| c!=p) return false;
}
if (p!=a[1][3]+a[2][2]+a[3][1]) return false;
return true;
}
void dfs(int now){
if (now>n){
if(check()){
cnt++;
for (int i=1;i<=3;++i){
for (int j=1;j<=3;++j){
ans[i][j]=a[i][j];
}
}
}
return ;
}
int x=p[now].first,y=p[now].second;
for (int k=1;k<=9;++k){
if (vis[k]) continue;
a[x][y]=k;
vis[k]=1;
dfs(now+1);
a[x][y]=0;
vis[k]=0;
}
}
signed main(){
for (int i=1;i<=3;++i){
for (int j=1;j<=3;++j){
cin>>a[i][j];
if (!a[i][j]) p[++ n]=make_pair(i,j);
vis[a[i][j]]=1;
}
}
dfs(1);
if (cnt!=1){
cout<<"Too Many";
return 0;
}else if (cnt==1){
for (int i=1;i<=3;++i){
for (int j=1;j<=3;++j){
cout<<ans[i][j]<<" ";
}
cout<<endl;
}
}
}