A题模拟看题意;
#include<iostream>
using namespace std;
const int N = 310;
int a[N];
int main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int l = 1,r = n;
for(int i = 1; i <= n; i++){
if(i != 1) cout << " ";
if(i % 2){
cout << a[l++];
}else cout << a[r--];
}
cout << endl;
}
return 0;
}
B题的意思是去掉一部分使数组只剩2020
很明显要么就是2020 或者 2.。。。020 或者
20.。。20,202.。。。0,2020.。。
#include<iostream>
using namespace std;
const int N = 310;
int a[N];
int main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
string a;
cin >> a;
bool flag = false;
int len = a.length();
if(a[0] == '2' && a[1] == '0' && a[2] == '2' && a[3] == '0'){
flag = true;
}else if(a[0] == '2' && a[1] == '0' && a[2] == '2' && a[len - 1] == '0'){
flag = true;
}else if(a[0] == '2' && a[1] == '0' && a[len - 2] == '2' && a[len - 1] == '0'){
flag = true;
}else if(a[0] == '2' && a[len - 3] == '0' && a[len - 2] == '2' && a[len - 1] == '0'){
flag = true;
}else if(a[len - 4] == '2' && a[len - 3] == '0' && a[len - 2] == '2' && a[len - 1] == '0'){
flag = true;
}
if(flag) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
C题思路 一定是位数越小的数越小 所以只要从9往1找就行了
#include<iostream>
#include<cstring>
using namespace std;
int flag[15];
int main(){
int t;
cin >> t;
while(t--){
memset(flag,0,sizeof flag);
int n;
cin >> n;
//123456789
if(n > 45){
cout << "-1" << endl;
continue;
}
for(int i = 9; i >= 1; i--){
if(n >= i){
flag[i] = i;
n -= i;
}
}
for(int j = 1; j <= 9; j++){
if(flag[j]) cout << flag[j];
}
cout << endl;
}
return 0;
}
D 因为数组的总值为sum 所以可以将数组分别分为 sum /2 + sum / 2 或者
sum / 3 + sum / 3 + sum / 3 直到 n个sum / n相加
并且sum 要是 i的整数倍 这块 直接跑一个on循环然后
判断是否成立即可
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 3010;
int n,m,t;
int a[N],b[N];
int res;
int main(){
cin >> t;
while(t--){
int n;
cin >> n;
int sum = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
}
for(int i = n; i >= 1; i--){
if(sum % i != 0){
continue;
}
ll h = sum / i;
ll change = n - i,cnt = 0,flag = 0;
for(int j = 1; j <= n; j++){
if(a[j] > h){
flag = 1;
break;
}
if(a[j] == h && (cnt == 0 || cnt == h)){
cnt = 0;
continue;
}
if(cnt == h) cnt = 0;
if(a[j] != h){
if(cnt != 0) change--;
cnt += a[j];
}
if(cnt > h || (a[j] == h && cnt < h && cnt != 0)){
flag = 1;
break;
}
}
if(!flag && change == 0){
cout << n - i << endl;
break;
}
}
}
return 0;
}
E1
大意就是在数组中随机取3个数 如果 max(a[i],a[j],a[k] - min(a[i],a[j],a[k]) <= 2即算一种 求一共有几种
这一看就直接sort就完事了然后双指针从头往后找一共有多少数
比如 1 1 2 2就可以变成取 第一个位置 和 第三个位置的方案 和 取 第1个位置
和取第 4 个位置的方案 那就是 cal(1,1) + cal(2,1);很明显 这样的方案一个个取特别耗时间 我们不如搞个数组 前缀和 将 1 - n的都开出来 整体的复杂度就是 3on
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
ll b[N];
int a[N];
int main(){
int t;
cin >> t;
for(int i = 1; i <= N - 1; i++){
b[i] = b[i - 1] + i;
}
while(t--){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
sort(a + 1,a + n + 1);
int l = 1,r = 3;
ll sum = 0;
while(l <= n - 2){
while(a[r] - a[l] <= 2 && r < n){
r++;
}
if(a[r] - a[l] > 2) r--;
sum += b[r - l - 1];
l++;
}
cout << 1ll * sum << endl;
}
return 0;
}
E2 的话就是翻版了
将3个变为n个 <= 2 变为 <= m
那么思路是一样的就是sort + 双指针
但是发现我们的前缀和已经不能用了
但是方案数还是一样的 就是 cal(n - 2,n - 2) + cal(n - 1,n - 2) + … + cal(k,n - 2)这个时候可以看出一个组合数学的常规公式
cal(n,m) = cal(n - 1,m - 1) + cal(n - 1,m);
所以上面的式子就可以转化为一个等式cal(k + 1,n - 1);
但是这样还是会超时 因为题目的数据 n 是2e5;
所以我们要想个logn的计算方式或者o1的计算方式
根据题目我们可以看到 m 是不超过100的 所以采用离线算法的化就是
n * m = 2e7 < 1e8 所以是可以实现的
所以我们只要预处理一个数组即可
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10,mod = 1e9 + 7;
int c[N][110];
int b[N];
int a[N];
ll qmi(ll a,ll b){
ll s = 1;
while(b){
if(b & 1){
s = s * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return s;
}
void init(){
c[0][0] = 1;
c[1][0] = 1,c[1][1] = 1;
for(int i = 2; i <= N - 1; i++){
c[i][0] = 1;
for(int j = 1; j <= min(i,100); j++){
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
}
}
ll cal(int x,int y){
return c[y][x];
}
int main(){
int t;
cin >> t;
init();
while(t--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
/*for(int i = 1; i <= N - 1; i++){
b[i] = b[i - 1] + cal(m - 2,m + i - 3);
cout << b[i] << " " << cal(m - 1,m + i - 2) << endl;
}// b[i] = cal(m - 1,m + i - 2) - cal(m - 2,m - 1); */
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
sort(a + 1,a + n + 1);
int l = 1,r = m;
ll sum = 0;
while(l <= n - m + 1){
while(a[r] - a[l] <= k && r < n){
r++;
}
while(a[r] - a[l] > k) r--;
//cout << r << " " << l << "Asd" << " " << a[r].x << " " << a[l].x << endl;
if(r - l >= m - 1) sum = (sum + cal(m - 1 ,r - l)) % mod;
l++;
}
printf("%lld\n",sum);
}
return 0;
}
F题的大意就是 使给出的片段中 能和别的片段都有交集的最少删减别的片段的个数
由题目可想到 我如果找到了那个最优的片段 我就要判断 有多少个片段的最右端值使小于我这个最优片段的最左端值 有多少片段的最左端值 是大于我这个最优片段的最大值 由这个思路 我们可以想到 枚举 + 二分 我用两个数组记录分别记录 我需要枚举的数组 另一个找最小的最右端的i值 和 找最大的左端值
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 10;
struct node{
int x,y;
}a[N],b[N];
int n,m;
bool cmp(node a,node b){
return a.x < b.x;
}
bool cmp1(node a,node b){
return a.y < b.y;
}
int c[N],d[N];
int main(){
int t;
cin >> t;
while(t--){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i].x >> a[i].y;
b[i].x = a[i].x,b[i].y = a[i].y;
}
int maxn = 0x3f3f3f3f;
sort(a + 1,a + n + 1,cmp);
for(int i = 1; i <= n; i++){
int sum = 0;
int l = 1,r = n;
while(l < r){
int mid = l + r + 1 >> 1;
if(b[i].y < a[mid].x){
r = mid - 1;
}else l = mid;
}
if(a[r].x > b[i].y){
sum += n - r + 1;
}else sum += n - r;
c[i] = sum;
}
sort(a + 1,a + n + 1,cmp1);
for(int i = 1; i <= n; i++){
int sum = 0;
int l = 1,r = n;
while(l < r){
int mid = l + r + 1 >> 1;
if(b[i].x > a[mid].y){
l = mid;
}else r = mid - 1;
}
if(a[l].y < b[i].x){
sum += l;
}else if(a[l].y == b[i].x){
sum += l - 1;
}
d[i] = sum;
}
for(int i = 1; i <= n; i++){
maxn = min(maxn,c[i] + d[i]);
}
cout << maxn << endl;
}
return 0;
}