Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)

Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)node

A. Math Problemios

題意:有t組數據,每組數據給出n個範圍[Li,Ri],求與n個範圍都有交集的最小範圍[Ans_L,Ans_R]的長度([L,R]的長度定義爲R-L)c++

思路:這個區間只要從全部區間右端點的最小值覆蓋到全部區間左端點的最大值便可。數組

#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int total;
    cin>>total;
    while(total--){
        int n,x,y;
        cin>>n;
        if(n==1) {
            cin>>x>>y;
            cout<<0<<endl;
            continue;
        }else{
            int qian=0,hou=1e9;
            for(int i=1;i<=n;i++){
                cin>>x>>y;
                qian=max(qian,x);
                hou=min(hou,y);
            }
            int ans=max(0,qian-hou);
            cout<<ans<<endl;
        }
    }
 
    return 0;
}
View Code

B. Boxide

題意:t組數據,每組數據給你n個信息,每一個信息qi告訴你當前序列的前i個數中最大的是qi。問可否構造數列。spa

思路:從第一位依次構造,若是當前位置給出的最大值沒出現過,那麼該最大值在此位置第一次出現,因此這個位置構造爲給出的最大值,若以前已經出現過,則從小於該最大值的數中選擇任意一個沒用過的數構造爲該位置出現的數。若沒有數能夠選擇,則該序列沒法構造。3d

#include<bits/stdc++.h>
using namespace std;
int shu[100005];
int ans[100005];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int total;
    cin>>total;
    while(total--){
        int n,get_num;
        bool flag=true;
        cin>>n;
        unordered_set<int>tree;
        set<int>num;
 
        for(int i=1;i<=n;i++){
            num.insert(i);
        }
        for(int i=1;i<=n;i++){
            cin>>shu[i];
        }
 
        for(int i=1;i<=n;i++){
            if(tree.find(shu[i])==tree.end()){
                tree.insert(shu[i]);
                num.erase(shu[i]);
                ans[i]=shu[i];
            }else{
                for(auto j=num.begin();j!=num.end();j++){
                    if((*j)<shu[i]){
                        ans[i]=(*j);
                        num.erase(j);
                        break;
                    }else if(*j>shu[i]){
                        flag=false;
                        break;
                    }
                }
                if(!flag) break;
            }
        }
        if(!flag) cout<<-1<<endl;
        else{
            for(int i=1;i<=n;i++){
                cout<<ans[i]<<" ";
            }cout<<endl;
        }
 
    }
    return 0;
}
View Code

C. Messyrest

題意:有一個房間,有許多的括號,長度爲n,如:「(())())(()」,如今讓你將括號整理乾淨,乾淨的標準是該括號序列合法,且它的全部前綴序列中剛好有k個是合法的序列。你的每次操做是將區間爲[L,R]的括號序列進行反轉(即交換L和R位置的括號,交換L+1和R-1位置的括號……直到區間中間),輸出操做次數和每次的操做區間(在n次內完成)。code

思路:題目保證有解,因此順序構造便可。能夠先構造k-1個單對的括號「()」,最後剩餘的n-2×(k-1)個括號構成大的嵌套括號「(((())))」,如n=8,k=2的「()(())()」能夠構造爲「()((()))」 。循環從第一位到最後一位,若是當前位置i的括號符合構造後的括號,則不需任何操做,若不符合構造後的括號,則日後循環找到第一個位置j,j位置的括號爲i位置須要的括號,並對[i,j]進行反轉操做。全部的[i,j]即爲答案。blog

#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int total;
    cin>>total;
    while(total--){
        int n,k,cont_l=0,cont_r=0;
        vector<pair<int,int> >ans;
        string s;
        cin>>n>>k;
        cin>>s;
        for(int i=0;i<s.size();i++){
            if((i+2)/2<k){
                if(i%2==0){
                    if(s[i]!='('){
                        for(int j=i+1;j<s.size();j++){
                            if(s[j]=='('){
                                ans.push_back(make_pair(i,j));
                                int l=i,r=j;
                                while(l<r){
                                    swap(s[l],s[r]);
                                    l++;r--;
                                }
                                break;
                            }
                        }
                    }
                }else{
                    if(s[i]!=')'){
                        for(int j=i+1;j<s.size();j++){
                            if(s[j]==')'){
                                ans.push_back(make_pair(i,j));
                                int l=i,r=j;
                                while(l<r){
                                    swap(s[l],s[r]);
                                    l++;r--;
                                }
                                break;
                            }
                        }
                    }
                }
            }else{
                int cont=(n-(k-1)*2);
                if(cont_l<cont){
                    if(s[i]!='('){
                        for(int j=i+1;j<s.size();j++){
                            if(s[j]=='('){
                                ans.push_back(make_pair(i,j));
                                int l=i,r=j;
                                while(l<r){
                                    swap(s[l],s[r]);
                                    l++;r--;
                                }
                                break;
                            }
                        }
                    }
                }else{
                    if(s[i]!=')'){
                        for(int j=i+1;j<s.size();j++){
                            if(s[j]==')'){
                                ans.push_back(make_pair(i,j));
                                int l=i,r=j;
                                while(l<r){
                                    swap(s[l],s[r]);
                                    l++;r--;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
        cout<<ans.size()<<endl;
        for(int i=0;i<ans.size();i++){
            cout<<ans[i].first+1<<" "<<ans[i].second+1<<endl;
        }
    }
    return 0;
}
View Code

D1. Optimal Subsequences (Easy Version)

題意:給出一個n個數的序列,有m次詢問,每次詢問知足條件的子序列的第posj個元素是什麼。條件爲:所選的子序列有k個元素,而且是全部有k個元素的子序列中全部元素和最大的且字典序最小的。

思路:簡單版本能夠隨便搞,能夠先用map記錄每一個數有多少個,而後對於每一次詢問能夠直接生成子序列,從最大的數開始選,若是序列剩餘須要的個數大於該數的個數,這個數的全部位置都選上,若是剩餘的個數小於該數的個數,則只選該數須要的前幾個,而後把生成的子序列按照位置排個序,輸出第posj個便可。

#include<bits/stdc++.h>
using namespace std;
int num[105];
map<int,vector<int>,greater<int> >tree;
vector<int>null_vec;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>num[i];
        if(tree.find(num[i])==tree.end()){
            tree[num[i]]=null_vec;
            tree[num[i]].push_back(i);
        }else{
            tree[num[i]].push_back(i);
        }
    }
    cin>>m;
    while(m--){
        int k,posj,cont=0;
        unordered_set<int>need;//num  need_num
        int special_num,special_num_need;
        vector<int>ans;
        cin>>k>>posj;
        for(auto i=tree.begin();i!=tree.end();i++){
            if(cont+(*i).second.size()>=k){
                special_num=(*i).first;
                special_num_need=k-cont;
                break;
            }else{
                need.insert((*i).first);
                cont+=(*i).second.size();
            }
        }
 
        int cont_small=0;
        for(int i=1;i<=n;i++){
            if(num[i]==special_num){
                if(cont_small<special_num_need){
                    ans.push_back(num[i]);
                    cont_small++;
                }
            }else{
                if(need.find(num[i])!=need.end()){
                    ans.push_back(num[i]);
                }
            }
        }
        cout<<ans[posj-1]<<endl;
    }
    return 0;
}
View Code

D2. Optimal Subsequences (Hard Version)

題意:和D1同樣,數據量變大了

思路:須要離線化操做,記錄下原數組每一個數的位置,而後把n個數按照數字從大到小,且數字相同時位置從小到大的順序排序,這個排好的順序就是咱們生成子序列時選擇的順序。以後把查詢按照k從小到大的順序排序。以後須要樹狀數組或線段樹維護一個[1,n]的前綴和。以後循環依次選擇以前排好序的數字,每次將該數字在最一開始的位置標爲1,更新前綴和,若是選擇的數字等於當前k,則進行查詢操做。查詢操做是一個二分查找,查找前綴和恰好爲posj位置的數,就爲答案。更新和查找完全部答案後按提問順序排序後輸出。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int k,pos,loc,ans;
}the_query[200005];
struct node_num{
    int num,loc;
}the_num[200005];
int tree[800020];
void update(int node,int l,int r,int aim){
    if(l==r){
        tree[node]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(aim<=mid){
        update(node<<1,l,mid,aim);
    }else{
        update(node<<1|1,mid+1,r,aim);
    }
    tree[node]=tree[node<<1]+tree[node<<1|1];
}
 
int query(int node,int q_l,int q_r,int l,int r){
    if(l>=q_l && r<=q_r){
        return tree[node];
    }
    int mid=(l+r)>>1,x=0,y=0;
    if(mid<q_l){
        y=query(node<<1|1,q_l,q_r,mid+1,r);
    }else if(mid>=q_r){
        x=query(node<<1,q_l,q_r,l,mid);
    }else{
        x=query(node<<1,q_l,q_r,l,mid);
        y=query(node<<1|1,q_l,q_r,mid+1,r);
    }
    return x+y;
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>the_num[i].num;
        the_num[i].loc=i;
    }
    cin>>m;
    for(int i=1;i<=m;i++){
        cin>>the_query[i].k>>the_query[i].pos;
        the_query[i].loc=i;
    }
    sort(the_num+1,the_num+1+n,[](const node_num &a,const node_num &b){
        if(a.num==b.num)
            return a.loc<b.loc;
        else
            return a.num>b.num;
    });
    sort(the_query+1,the_query+1+m,[](const node &a,const node &b){return a.k<b.k;});
    for(int i=1,j=1;i<=n;i++){
        update(1,1,n,the_num[i].loc);
        while(j<=m && i==the_query[j].k){
            int l=1,r=n;
            while(l<r){
                int mid=(l+r)>>1;
                int judge=query(1,1,mid,1,n);
                if(judge<the_query[j].pos){
                    l=mid+1;
                }else if(judge==the_query[j].pos){
                    r=mid;
                }else{
                    r=mid-1;
                }
            }
            the_query[j].ans=l;
            j++;
        }
    }
    sort(the_query+1,the_query+1+m,[](const node &a,const node &b){return a.loc<b.loc;});
    sort(the_num+1,the_num+1+n,[](const node_num &a,const node_num &b){return a.loc<b.loc;});
    for(int i=1;i<=m;i++){
        cout<<the_num[the_query[i].ans].num<<endl;
    }
    return 0;
}
View Code

E. Arson In Berland Forest

 

F1. Wrong Answer on test 233 (Easy Version) F2. Wrong Answer on test 233 (Hard Version)

題意:一個卷子n個選擇題,每一個選擇題k個選項,你先答了一遍提,而後將全部答案一次日後挪了一題(1題答案對應2題,2題答案對應3題……n題答案對應1題),你知道每一個題的正確答案,問轉換後得分大於轉換前的狀況共有多少種可能,答案mod 998244353

思路:數學題,對於答案,咱們能夠反向考慮,先求得有多少種答題狀況轉換後分值沒有變化,記爲ans,因此剩餘狀況爲(nk-ans)個,包含轉換後分值變高和轉換後分值變低,由於這兩種狀況是對稱的,因此轉換後分值變高的狀況爲 (nk-ans)/2 ,因此問題轉換成計算有多少種狀況轉化後分值無變化。先統計一遍有多少個位置不知足h[i]==h[i+1](注意是個環),記爲num,則有n-num個位置知足h[i]==h[i+1],接下來咱們考慮,若是分值沒變化,那麼我有i道題轉化後得分由0變1,我就得由i道題轉化後得分由1變0,因此是 C(num,i)*C(num-i,i),餘下的有num-2i個題要轉化後結果同樣,爲(num-2i)k-2,最後的(n-num)個能夠任選就是(n-num)k,因此最終答案爲i從0循環到num/2,對C(num,i)*C(num-i,i)*(num-2i)k-2*(n-num)k求累加和即爲保持不變的狀況個數ans,再用乘法逆元求出(nk-ans)/2 的值即爲最終答案。

代碼前面大部分爲組合數模板,核心代碼在main中。

#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
long long table[200005],h[200005];
void cal_table(int n){      
    table[0]=1;
    for(int i=1;i<=n;i++){
        table[i]=table[i-1]*i%mod;
    }
    return;
}
 
void extend_gcd(long long a,long long b,long long &d,long long &x,long long &y){   
    if(b==0){
        d=a;x=1;y=0;
    }else{
        extend_gcd(b,a%b,d,x,y);
        long long t=x;
        x=y;                  
        y=t-(a/b)*(y);
    }
    return;
}
 
long long inv(long long a,long long b){     
    long long x,y,d;
    extend_gcd(a,b,d,x,y);
    return (d==1)? (x+b)%b : -1;  
}
 
long long C(long long n,long long m){       
    if(n<0 || m<0 || m>n) return 0;
    return table[n]*inv(table[m],mod)%mod*inv(table[n-m],mod)%mod;
}
 
long long qpow(long long a,long long b){
    long long sum=1;
    while(b){
        if(b&1){
            sum=(sum*a)%mod;
            b--;
        }
        b/=2;
        a=a*a%mod;
    }
    return sum;
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    long long n,k,num=0,ans=0;
    cin>>n>>k;
    cal_table(n);
    for(int i=1;i<=n;i++){
        cin>>h[i];
    }
    h[n+1]=h[1];
    for(int i=2;i<=n+1;i++){
        if(h[i-1]!=h[i]) num++;
    }
    for(long long i=0;2*i<=num;i++){
        ans=(ans+(((C(num,i)*C(num-i,i)%mod)*qpow(k-2,num-2*i)%mod)*qpow(k,n-num)%mod))%mod;
    }
    long long ni=qpow(2,mod-2);
    cout<<((qpow(k,n)-ans+mod)*ni)%mod<<endl;
    return 0;
}
View Code

其實F1還能夠用dp去作,可是我不太理解,就沒有寫出來。

相關文章
相關標籤/搜索