【Codeforces 1462】Codeforces Round #690 (Div. 3) | 全题解

阿克快乐,阿克了DIV3

比赛连接:https://codeforces.com/contest/1462

这场比赛蛮简单的.

目录

A. Favorite Sequence

题目大意:

题目思路:

Code:

B. Last Year's Substring

题目大意:

题目思路:

Code:

C. Unique Number

题目大意:

题目思路:

Code:

D. Add to Neighbour and Remove

题目大意:

题目思路:

Code:

E2. Close Tuples (hard version)

题目大意:

题目思路:

Code:

F. The Treasure of The Segments

题目大意:

题目思路:

Code:


A. Favorite Sequence

题目大意:

给出一个序列,按照题目顺序左右左右输出序列

题目思路:

水题,按照题意模拟

Code:

ll n,m,p;
ll a[maxn];
int main(){
    int T;scanf("%d",&T);
    while(T--){
        read(n);
        for(int i=1;i<=n;i++) read(a[i]);
        int l = 1,r = n;
        while(l<r){
            printf("%lld %lld ",a[l],a[r]);
            l++;
            r--;
        }
        if(l == r) printf("%lld\n",a[l]);
        else printf("\n");
    }
    return 0;
}

B. Last Year's Substring

题目大意:

给出一个数字字符串,问能否通过至多一次的删除子串操作,使最后字符串变为"2020"

题目思路:

考虑最多一次操作,所以只能删除中间的子串,所以判断一下边界就可以了

Code:

ll n,m,p;
char s[maxn];
int main(){
    int T;scanf("%d",&T);
    while(T--){
        read(n);
 
        scanf("%s",s+1);
        if(n<4) printf("NO\n");
        else{
            if(s[1] == '2' && s[2] == '0' && s[3] == '2' && s[4] == '0') printf("YES\n");
            else if(s[1] == '2' && s[2] == '0' && s[3] == '2' && s[n] == '0') printf("YES\n");
            else if(s[1] == '2' && s[2] == '0' && s[n-1] == '2' && s[n] == '0') printf("YES\n");
            else if(s[1] == '2' && s[n-2] == '0' && s[n-1] == '2' && s[n] == '0') printf("YES\n");
            else if(s[n-3] == '2' && s[n-2] == '0' && s[n-1] == '2' && s[n] == '0') printf("YES\n");
            else printf("NO\n");
        }
    }
    return 0;
}

C. Unique Number

题目大意:

问数位和等于x,并且各位数字互不相同的,最小数字

题目思路:

考虑数字互不相同,所以一共就有2的10次方就可能

2进制枚举一下即可

Code:

int main(){
    int T;scanf("%d",&T);
    while(T--){
        read(n);
        int ans = 1e9+7;
        for(int i=0;i<(1<<10);i++){
            int res = 0,temp = 0;
            for(int k=0;k<10;k++){
                if(i>>k&1){
                    res += k;
                    a[++temp] = k;
                }
            }
            if(res == n){
                if(a[1] == 0) swap(a[1],a[2]);
                int tempx = 0;
                for(int k=1;k<=temp;k++) tempx = tempx*10+a[k];
                ans = min(ans,tempx);
            }
        }
        printf("%d\n",ans == mod?-1:ans);
    }
    return 0;
}

D. Add to Neighbour and Remove

题目大意:

抽象一下:给出一个序列,问你最多能划分为多少段,使得每段的和,相等。

最后答案即为:n-段数

题目思路:

有一个朴素的思路,直接考虑最终状态的和,那么这个和必然是总和的因子

每个和都去check一下就好了

此时考虑如果总和很大怎么办?

这里给出一种区间dp的写法,与s无关..,思路也蛮简单的,考虑dp[i][k]代表前i个数分成k段的相等值为多少

然后得出一个n^3的暴力,用map优化掉即可

这就显得我很呆..

Code:

ll n,m,p;
ll num[maxn],sum[maxn];
int dp[3005][3005];
int pre[3005];
unordered_map<ll,int>mp[3005];
int main(){
    int T;scanf("%d",&T);
    while(T--){
        read(n);
 
        for(int i=1;i<=n;i++) {
            read(num[i]);
            sum[i] = sum[i-1]+num[i];
            mp[i].clear();
        }
        int mx = 1;
        for(int i=1;i<=n;i++)
            for(int k=1;k<=n;k++)
                dp[i][k] = -1;
 
       for(int i=1;i<=n;i++){
            dp[i][1] = sum[i];
            for(int k=2;k<=i;k++){
                int op = mp[k-1][sum[i]];
                if(op) dp[i][k] = sum[i]-sum[op];
            }
            for(int k=1;k<=i;k++)
                if(~dp[i][k]) mp[k][dp[i][k]+sum[i]] = i;
       }
        for(int i=n,k=0;i>=1;i--,k++){
            if(dp[n][i]!=-1){
                printf("%d\n",k);
                break;
            }
        }
    }
    return 0;
}

E2. Close Tuples (hard version)

题目大意:

给出一个长度为n序列,询问有多少个长度为m的子序列,满足序列最大值-序列最小值<=k

题目思路:

枚举最终答案的最小值,然后进行组合数判断即可

假设当前枚举的最小值为x,那么需要知道[x,x+k]有多少个数:这个操作尺取双指针也可以完成,为了简单写了个树状数组,结果想起来二分也可以..

然后分别对最小值的个数,进行枚举即可

Code:

ll n,m,p;
ll num[maxn];
ll sum[maxn];
int vis[maxn];
ll fac[maxn];
ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }return ans;
}
ll cal(int m,int n){
    if(m<n) return 0;
    return ((fac[m]*qpow(fac[m-n],mod-2))%mod*(qpow(fac[n],mod-2))%mod)%mod;
}
void add(int pos){
    while(pos <= n){
        sum[pos] ++;
        pos += pos&-pos;
    }
}
ll GetSum(int pos){
    ll ans = 0;
    while(pos){
        ans += sum[pos];
        pos -= pos&-pos;
    }return ans;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        read(n);read(m);read(p);
        for(int i=1;i<=n;i++) read(num[i]);
        for(int i=1;i<=n;i++) sum[i] = 0,vis[i] = 0;
        for(int i=1;i<=n;i++) vis[num[i]]++;
        fac[0] = 1;
        for(int i=1;i<=n;i++) fac[i] = (fac[i-1]*i)%mod;
        for(int i=1;i<=n;i++) add(num[i]);
        ll ans = 0;
        for(int i=1;i<=n;i++){
            if(vis[i]){
                ll temp = GetSum(min(i+p,n)) - GetSum(i-1);
                for(int k=1;k<=min(m,vis[i]*1ll);k++){
                    ans = (ans + (cal(vis[i],k)*cal(temp-vis[i],m-k))%mod)%mod;
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

F. The Treasure of The Segments

题目大意:

定义一个线段集是good,当且仅当存在一个线段与其他线段全部相交,问最少删除几条线段使得这个线段集good

题目思路:

很明显的思路,枚举是哪一个线段使得这个线段集good

所以就要求得与该线段相交的有哪些线段,用【总数-与该线段相交的线段数】,即为不相交的线段数

最后对于每一个线段的答案,取一个min就好了

这个题好多种做法,因为坐标太大,直接用主席树动态开点了(懒得离散化)

Code:

/*** keep hungry and calm CoolGuang!  ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 2e5+700;
const int mod= 1e9+7;
const int up = 1e9;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
struct node{
    int a,b;
    bool friend operator<(node a,node b){
        if(a.a == b.a) return a.b < b.b;
        return a.a < b.a;
    }
}q[maxn];
int pre[maxn],suf[maxn];
ll sum[maxn];
int cnt = 0;
struct Tree{
    int l,r,w;
}t[maxn*35];
int root[maxn];
void Insert(int &now,int pre,ll l,ll r,ll pos){
    t[now = ++cnt] = t[pre];
    t[now].w ++;
    if(l == r) return ;
    ll mid = (l+r)/2;
    if(pos <= mid) Insert(t[now].l,t[pre].l,l,mid,pos);
    else Insert(t[now].r,t[pre].r,mid+1,r,pos);
}
int Find(int now,ll l,ll r,ll x,ll y){
    if(x<=l && y>=r) return t[now].w;
    ll mid = (l+r)/2;
    int ans = 0;
    if(x<=mid) ans += Find(t[now].l,l,mid,x,y);
    if(y>mid) ans += Find(t[now].r,mid+1,r,x,y);
    return ans;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        read(n);
        for(int i=1;i<=n;i++){
            read(q[i].a);
            read(q[i].b);
        }
        sort(q+1,q+1+n);
        cnt = 0;
        root[0] = 0;
        for(int i=1;i<=n;i++){
            pre[i] = Find(root[i-1],1,2e9,q[i].a,q[i].b);
            Insert(root[i],root[i-1],1,2e9,q[i].b);
        }
        cnt = 0;
        root[n+1] = 0;
        for(int i=n;i>=1;i--){
            suf[i] = Find(root[i+1],1,2e9,q[i].a,q[i].b);
            Insert(root[i],root[i+1],1,2e9,q[i].a);
        }
        ll ans = INF;
        for(int i=1;i<=n;i++)
            ans = min(ans,n-1-pre[i]-suf[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/111274704