Educational Codeforces Round 168 (Rated for Div. 2) 补题记录(A~E)

A

直接暴力枚举字符添加在哪里,以及添加的字符是什么即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500100;
signed main(){
    int T;
    cin>>T;
    while(T--){
        string s;
        cin>>s;
        string ans;
        int mi=-1e18;
        for(int i=0;i<=s.size();++i){
            for(char ch='a';ch<='z';++ch){
                string aa;
                for(int j=0;j<i;++j)aa+=s[j];
                aa+=ch;
                for(int j=i;j<s.size();++j)aa+=s[j];
                int cost=2;
                for(int i=1;i<aa.size();++i)
                    if(aa[i]==aa[i-1])++cost;
                    else ++cost,++cost;
                if(mi<cost)
                    mi=cost,ans=aa;
            }
        }
        cout<<ans<<'\n';
    }
}

B

枚举每一个格子,现在需要计算当前的连通块数目:考虑使用动态维护图连通性的科技 考虑判断一个字符被染色之后对答案造成的贡献:如果这个格子位于角落那么显然不可以,否则这个格子和 \(3\) 个格子之间有边相连。若这个格子为这三个格子之间的割点,即删除这个格子之后和他相邻的三个格子会被分为三个互不连通的连通块,那么这个格子就对答案产生了 \(1\) 的贡献。

问题在于判定格子是否会产生贡献:若其周围三个格子都为空,且其左上(下)角和右上(下)角的格子都不空,那么让这个格子不空之后可以恰好产生三个连通块。因此直接判断即可。时间复杂度为 \(O(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500100;
char a[2][N];
signed main(){
    int T;
    cin>>T;
    while(T--){
        int n;cin>>n;
        for(int i=0;i<2;++i)scanf("%s",a[i]);
        int xx=0;for(int i=0;i<2;++i)for(int j=0;j<n;++j)
        if(a[i][j]=='.')++xx;if(xx==0)cout<<"0\n";
        else{
            int cnt=0;
            for(int i=0;i<2;++i)for(int j=0;j<n;++j)
            if(a[i][j]=='.'){
                if(i==0){
                    if((j!=0&&a[1][j-1]=='x'&&a[0][j-1]!='x')&&(j!=n-1&&a[1][j+1]=='x'&&a[0][j+1]!='x')&&a[1][j]!='x')++cnt;
                }else{
                    if((j!=0&&a[0][j-1]=='x'&&a[1][j-1]!='x')&&(j!=n-1&&a[0][j+1]=='x'&&a[1][j+1]!='x')&&a[0][j]!='x')++cnt;
                }
            }
            cout<<cnt<<'\n';
        }
    }
}

C

反悔贪心板子题。考虑尽量的让当前字符为右括号。如果到某一个前缀的时候右括号数量大于左括号数量,那么就贪心的让当前最后一个被变为右括号的括号变为左括号。这个变为右括号的括号集合用一个栈维护即可。时间复杂度为 \(O(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500100;
char s[N];
signed main(){
    int T;
    cin>>T;
    while(T--){
        int n;cin>>n;
        scanf("%s",s+1);
        int cnt=0;
        int c1=n/2,c2=n/2;
        for(int i=1;i<=n;++i)
            if(s[i]=='(')--c1;
            else if(s[i]==')')--c2;
        int pre=0;
        stack<int>laright;
        for(int i=1;i<=n;++i){
            if(s[i]=='_'){
                if(!pre)pre=1,s[i]='(',--c1;
                else if(!c2)++pre,s[i]='(',--c1;
                else --pre,--c2,s[i]=')',laright.emplace(i);
            }else if(s[i]=='(')++pre;
            else{
                --pre;
                if(pre<0){
                    pre+=2;
                    int t=laright.top();laright.pop();
                    s[t]='(';--c1,++c2;
                }
            }
        }
        stack<int>stk;
        for(int i=1;i<=n;++i)
            if(s[i]=='(')stk.push(i);
            else{
                cnt+=i-stk.top();
                stk.pop();
            }
        cout<<cnt<<'\n';
    }
}

D

不是,这题怎么才 \(1500\)??啊????(注:差点没切,wa 逆天点 #19)

看到最大值考虑二分然后将问题转化为判定问题。设现在判定答案 \(p\) 是否合法。

此时根节点值必须至少为 \(p\),且其他结点的值必须至少为 \(0\)

现在强制令根节点的值不小于 \(p\),此时其他结点的值全部减去 \(p\)

然后就是计算是否有一组方案满足其他结点的值全部大于等于 \(0\)。这是简单的。

把树从上往下遍历一遍。对于每一个非叶子结点 \(p\),记录其子树除她以外的结点集合为 \(SV\),则 \(SV\) 集合中每一个元素都需要为 \(p\) 结点做出贡献。记录贡献可以使用 dfs 序套线段树,也可以直接打一个标记然后 dfs 一遍。最后只需要判断虽有的叶子结点的值是否都非负即可。时间复杂度为 \(O(n\log n)\) 但是不知道为什么跑的十分的慢。

然后这个题难点是溢出问题,建议使用 Python

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500100;
int a[N],n,jia[N];
vector<int>z[N];
__int128 b[N];
void dfs(int u,int fa,__int128 qian){
    b[u]-=qian;
    if(qian>1e12&&b[u]<0){
        b[u]=-233;
        return;
    }
    if(b[u]>=0){
        for(auto&v:z[u])
            if(v!=fa)dfs(v,u,qian);
    }else{
        int ok=1;
        for(auto&v:z[u])
            if(v!=fa)dfs(v,u,qian-b[u]),ok=0;
        if(!ok)
        b[u]=0;
    }
}
//判定根节点的值最后是否可以为p
bool check(int p){
    for(int i=1;i<=n;++i)b[i]=a[i],jia[i]=0;
    int jiax=max((__int128)(0),p-b[1]);
    b[1]+=jiax;
    for(int i=2;i<=n;++i)b[i]-=jiax;
    dfs(1,0,0);
    int ok=1;
    for(int i=1;i<=n;++i)if(b[i]<0){ok=0;break;}
    if(b[1]<p)ok=0;
    return ok;
}
signed main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int T;
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;++i)cin>>a[i],z[i].clear();
        for(int i=2;i<=n;++i){
            int u=i,v;cin>>v;
            z[u].emplace_back(v),z[v].emplace_back(u);
        }
        int l=1,r=(int)(2e18)+2333,best=0;
        while(l<=r){
            int mid=l+r>>1;
            if(check(mid))best=mid,l=mid+1;
            else r=mid-1;
        }
        cout<<best<<'\n';
    }
}

E

Upd:FST 了,还需要继续卡常,很懒所以不卡了

使用伟大的数据结构。考虑对于每一个不同的 \(k\) 值,都扫描出答案:每一次二分出一段区间 \([l,r]\) 满足这一段区间内所有的位置 \(i\),这个人的等级都为 \(Lv\)。然后直接暴力二分扫描覆盖,判断答案是否成立即可。

更具体的说,二分一段区间 \([l,r]\) 可以每一次二分出对于当前的左端点 \(L\),最大的右端点 \(R\) 满足 \(L\sim R\) 一段区间中 \(\ge Lv\) 的数不超过 \(k\)。这个东西可以使用可持久化线段树来解决。看起来十分的慢。但是可以发现最外层的两次枚举并不是 \(O(n^2)\) 而是调和级数的。因此最终总的时间复杂度为 \(O(n\log^3n)\),大力卡常之后可以 \(3.8s\) 卡过。

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
using namespace std;
const int N=200100;
vector<pair<int,int>>scc[N];
struct qwq{
    int l,r,sum;
}z[N<<5];
int idx,a[N],rt[N],res[N];
int modify(int l,int r,int &rt,int p,int v){
    int pp=++idx;z[pp]=z[rt];
    if(l==r){
        z[pp].sum+=v;
        return pp;
    }
    int mid=l+r>>1;
    if(p<=mid)
        z[pp].l=modify(l,mid,z[rt].l,p,v);
    else
        z[pp].r=modify(mid+1,r,z[rt].r,p,v);
    z[pp].sum=z[z[pp].l].sum+z[z[pp].r].sum;
    return pp;
}
int query(int p,int l,int r,int k){
    if(l>=k)
        return z[p].sum;
    int mid=l+r>>1;
    if(k>mid)
        return query(z[p].r,mid+1,r,k);
    return query(z[p].l,l,mid,k)+z[z[p].r].sum;
}
signed main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int n,q;cin>>n>>q;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=q;++i){
        int v,x;cin>>v>>x;
        scc[x].emplace_back(v,i);
    }
    for(int i=1;i<=n;++i)
        rt[i]=modify(1,200001,rt[i-1],a[i],1);
    for(int i=1;i<=n;++i)
        sort(scc[i].begin(),scc[i].end());
    for(int i=1;i<=n;++i)
        if(scc[i].size()){
            int p=1,nowlevel=1;
            while(p<=scc[i].back().first){
                int l=p,r=scc[i].back().first,best=-1;
                //计算区间[l,r]中有多少个数>=now
                int presolve=query(rt[p-1],1,200001,nowlevel);
                while(l<=r){
                    int mid=l+r>>1;
                    if(query(rt[mid],1,200001,nowlevel)-presolve>=i)
                        best=mid,r=mid-1;
                    else
                        l=mid+1;
                }
                int lp;
                if(best==-1)
                    lp=n;
                else
                    lp=best;
                l=0,r=scc[i].size()-1,best=-1;
                while(l<=r){
                    int mid=l+r>>1;
                    if(scc[i][mid].first>=p)
                        best=mid,r=mid-1;
                    else
                        l=mid+1;
                }
                if(best!=-1){
                    for(int j=best;j<scc[i].size();++j)
                        if(scc[i][j].first>lp)break;
                        else if(a[scc[i][j].first]>=nowlevel)
                            res[scc[i][j].second]=1;
                }
                p=lp+1;
                ++nowlevel;
            }
        }
    for(int i=1;i<=q;++i)
        if(res[i])cout<<"YES\n";
        else cout<<"NO\n";
}
posted @ 2024-07-31 10:37  yhbqwq  阅读(52)  评论(2编辑  收藏  举报