【Codeforces1462】CodeforcesRound#690(Div。3)|完全なソリューション

ハッピーアケ、アケはDIV3を持っています

コンテスト接続:https//codeforces.com/contest/1462

このゲームはとてもシンプルです。

目次

A.好きなシーケンス

本旨:

質問のアイデア:

コード:

B.昨年の部分文字列

本旨:

質問のアイデア:

コード:

C.一意の番号

本旨:

質問のアイデア:

コード:

D.ネイバーに追加して削除する

本旨:

質問のアイデア:

コード:

E2。タプルを閉じる(ハードバージョン)

本旨:

質問のアイデア:

コード:

F.セグメントの宝物

本旨:

質問のアイデア:

コード:


 

A.好きなシーケンス

本旨:

シーケンスを指定して、質問の順序でシーケンスを左右に出力します

質問のアイデア:

水の質問、質問の意味に従ってシミュレートします

コード:

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.昨年の部分文字列

本旨:

数字の文字列を入力し、部分文字列を最大で1回削除して、最終的な文字列を「2020」にすることができるかどうかを尋ねます。

質問のアイデア:

最大で1つの操作を検討してください。削除できるのは中央の部分文字列だけなので、境界を判断するだけです。

コード:

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.一意の番号

本旨:

桁の合計がxに等しく、桁が互いに異なるかどうかを尋ねます。最小の数です。

質問のアイデア:

数値が異なることを考慮すると、合計で2の10乗が可能です。

バイナリで列挙するだけです

コード:

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.ネイバーに追加して削除する

本旨:

要約:シーケンスが与えられたら、各セグメントの合計が等しくなるように、分割できるセグメントの数を尋ねます。

最終的な答えは次のとおりです。n-セグメントの数

質問のアイデア:

最終状態の合計を直接考慮するという簡単な考え方があり、この合計は合計の因数でなければなりません

一人一人をチェックするだけです

合計が大きい場合はどうなるでしょうか。

これは、sとは関係のない区間dpを記述する方法です。考え方は非常に単純です。dp[i] [k]は、k個のセグメントに分割された最初のiの等しい値を表すと考えてください。

次に、n ^ 3の暴力を取得し、マップを使用して最適化します

これは私を非常に馬鹿にします。

コード:

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。タプルを閉じる(ハードバージョン)

本旨:

長さnのシーケンスが与えられた場合、シーケンスの最大値-シーケンスの最小値<= kを満たす長さmのサブシーケンスの数を尋ねます。

質問のアイデア:

最終回答の最小値を列挙し、組み合わせの数を判断します

現在の列挙の最小値がxであると仮定すると、[x、x + k]がいくつあるかを知る必要があります。この操作は、ダブルポインターを使用して実行することもできます。ツリー配列を単純に記述するために、あなたがそれを考えるならば、結果は2つに分けることができます..

次に、それぞれ最小値の数を列挙します

コード:

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.セグメントの宝物

本旨:

他のすべての線分と交差する線分がある場合にのみ、適切な線分セットを定義します。少なくともいくつかの線分を削除して、この線分セットを適切なものにします。

質問のアイデア:

明らかなアイデアは、どの線分がこの線分セットを適切にするかを列挙することです。

したがって、線分と交差するために必要な線分は、[総数-この線分と交差する線分の数]を使用します。これは、ばらばらの線分の数です。

最後に、各線分の答えについては、1分かかります

座標が大きすぎて、椅子ツリーを使用してポイントを動的に開くため、この質問を行う方法はたくさんあります(離散化するには怠惰すぎます)

コード:

/*** 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