首页 > 其他分享 >AtCoder Beginner Contest 370 补题记录(A~F)

AtCoder Beginner Contest 370 补题记录(A~F)

时间:2024-09-07 22:51:56浏览次数:4  
标签:AtCoder cout Beginner nxt int cin i1 补题 mod

A

signed main(){
    int l,r;cin>>l>>r;
    if(!(l==1&&r==1||l!=1&&r!=1)){
        if(l==1)cout<<"Yes\n";
        else cout<<"No\n";
    }
    else cout<<"Invalid\n";
}

B

signed main(){
    int n;cin>>n;
    F(i,1,n)F(j,1,i)cin>>a[i][j];
    int now=1;
    F(i,1,n)
        if(i>=now)now=a[i][now];
        else now=a[now][i];
    cout<<now<<'\n';
}

C

观察到 \(n\) 很小,因此考虑暴力枚举每一个满足 \(s_i\neq t_i\) 的 \(i\) 并计算出修改这个位置的 \(s_i\) 为 \(t_i\) 之后的字符串,将所有这样的字符串集合 \(S\) 按照字典序从小到大排序之后贪心选择当前字典序最小的即可。时间复杂度为 \(O(n^3\log n)\)。

const int N=1000100,mod=1e9+7;
char s[N],t[N];
signed main(){
    scanf("%s%s",s+1,t+1);
    vector<string>v;
    int n=strlen(s+1);
    int cnt=0;
    F(i,1,n)
        if(s[i]!=t[i])++cnt;
    F(i,1,cnt){
        vector<pair<string,int> > tv;
        F(p,1,n){
            if(s[p]!=t[p]){
                char tt=s[p];
                s[p]=t[p];
                string o;
                F(j,1,n)o.push_back(s[j]);
                tv.eb(o,p);
                s[p]=tt;
            }
        }
        sort(rng(tv));
        s[tv[0].second]=t[tv[0].second];
        v.eb(tv[0].first);
    }
    cout<<v.size()<<'\n';
    for(auto &x:v)
        cout<<x<<'\n';
}

D

考虑开 \(2n\) 个 set 分别记录 每一行 / 每一列 所有有障碍物的位置,然后查询位置 \((x,y)\):

  • 若 \((x,y)\) 有障碍物,则直接删除。
  • 否则在 set 上二分找出 \((x,y)\) 上面、下面、左边、右边第一个障碍物的位置。

然后更新 \(2n\) 个 set 即可。注意到一次最多只可能删除 \(4\) 个位置,且每一个位置最多只会被删除一次,因此总的时间复杂度为 \(O((nm+q)\log nm)\)。

set<int>s1[N],s2[N],s3[N],s4[N];
signed main(){
    // freopen(".out","w",stdout);
    int n,m,q;cin>>n>>m>>q;
    mp.resize(n+3);
    zuo.resize(n+3);
    you.resize(n+3);
    shang.resize(m+3);
    xia.resize(m+3);
    set<PII>se;
    F(i,1,n)F(j,1,m)se.insert({i,j});
    F(i,1,n)F(j,1,m)s1[i].insert(j);
    F(i,1,m)F(j,1,n)s2[i].insert(j);
    while(q--){
        int x,y;cin>>x>>y;
        if(se.count({x,y})){
            s1[x].erase(y);
            s2[y].erase(x);
            se.erase({x,y});
        }else{
            auto i1=s1[x].upper_bound(y);
            vector<PII>cx;
            if(i1!=s1[x].end()){
                int _x=x,_y=*i1;
                cx.eb(_x,_y);
                se.erase({_x,_y});
            }
            if(i1!=s1[x].begin()){
                --i1;
                int _x=x,_y=*i1;
                cx.eb(_x,_y);
                se.erase({_x,_y});
            }
            i1=s2[y].upper_bound(x);
            vector<PII>cy;
            if(i1!=s2[y].end()){
                int _x=*i1,_y=y;
                cy.eb(_x,_y);
                se.erase({_x,_y});
            }
            if(i1!=s2[y].begin()){
                --i1;
                int _x=*i1,_y=y;
                // cout<<"wtf "<<_x<<' '<<_y<<'\n';
                cy.eb(_x,_y);
                se.erase({_x,_y});
            }
            for(auto &[_x,_y]:cx)
                s1[x].erase(_y),s2[_y].erase(x);
            for(auto &[_x,_y]:cy)
                s2[y].erase(_x),s1[_x].erase(y);
        }
        // for(auto &[x,y]:se)
        //     cout<<"qwq "<<x<<' '<<y<<'\n';
        // cout<<'\n';
    }
    cout<<se.size()<<'\n';
}

E

设 \(f_i\) 表示当前枚举到的连续子序列以 \(i\) 结尾的不同方案数,显然答案为 \(\sum\limits_{i=1}^n f_i\),若 \(1\le j\le i\) 且 \(j\sim i\) 一段区间的和不为 \(k\) 则 \(f_i\leftarrow f_j\)。因此可以做到 \(O(n^2)\)。

考虑优化,设 \(g_i\) 表示当前前缀和为 \(i\) 的 \(f\) 值的和,则 \(g\) 的转移容易,\(f_i\) 的转移可以优化为 \(f_i=(\sum\limits_{j=0}^{i-1} f_j)-g_{(\sum\limits_{j=1}^i a_i)-k}\)

其中 \(\sum\limits_{j=0}^{i-1} f_j\) 和 \(\sum\limits_{j=1}^i a_i\) 都可以处理出,因此这样计算时间复杂度为 \(O(n\log n)\),瓶颈在于 map

\(O(n^2)\) 代码

const int N=500100,mod=998244353;
int a[N],s[N],n,k,f[N];
signed main(){
    cin>>n>>k;
    F(i,1,n)cin>>a[i],s[i]=s[i-1]+a[i];
    f[0]=1;
    F(i,1,n){
        F(j,1,i)
            if(s[i]-s[j-1]!=k)
                f[i]=(f[i]+f[j-1])%mod;
    }
    cout<<f[n]<<'\n';
}

\(O(n\log n)\) 代码

const int N=500100,mod=998244353;
int a[N],s[N],n,k,f[N];
map<int,int>g;
signed main(){
    cin>>n>>k;
    F(i,1,n)cin>>a[i],s[i]=s[i-1]+a[i];
    f[0]=1;
    g[0]=1;
    int all=1;
    F(i,1,n){
        f[i]=all-g[s[i]-k];
        f[i]%=mod;f[i]+=mod;f[i]%=mod;
        all+=f[i];
        all%=mod;
        g[s[i]]+=f[i];
    }
    cout<<f[n]<<'\n';
}

F

首先套路的破换成链,然后二分答案 \(p\)。

对于每一个二分的答案 \(p\),考虑先二分出 \(f_{i,0}\) 表示 \(i\) 之后第一个满足 \(\sum\limits_{j=i}^{f_{i,0}} a_j\ge p\) 的 \(f_{i,0}\),然后倍增的设 \(f_{i,j}\) 表示 \(i\) 之后满足 \(2^j\) 次上述条件的下标为 \(f_{i,j}\)。特殊的若不可以满足则令 \(f_{i,j}=-1\)。

然后考虑枚举起点,直接暴力倍增计算出 \(k\) 次操作之后的答案即可。时间复杂度为 \(O(n\log^2n)\)。代码有点混乱。

const int N=400100,mod=998244353;
int a[N],s[N],n,k,now,nxt[N][20],res;
bool chk(int p){
    memset(nxt,-1,sizeof nxt);
    F(i,1,n+n){
        int l=i,r=i+n-1,best=-2;
        r=min(r,n+n);
        while(l<=r){
            int mid=l+r>>1;
            if(s[mid]-s[i-1]>=p)best=mid,r=mid-1;
            else l=mid+1;
        }
        nxt[i][0]=best+1;
    }
    nxt[n+n+1][0]=n+n+1;
    F(i,1,19)
        F(j,1,n+n+1)
            if(~nxt[j][i-1])
                nxt[j][i]=nxt[nxt[j][i-1]][i-1];
    // F(i,1,n+n)
    //     cout<<i<<": "<<nxt[i][0]<<' '<<nxt[i][1]<<" qwq\n";
    int cnt=0;
    F(i,1,n){
        int x=i;
        G(j,19,0)
            if(k>>j&1){
                x=nxt[x][j];
                if(x==-1||x>i+n)break;
            }
        if(x!=-1&&x<=i+n)
            ++cnt;
    }
    if(cnt)
        res=n-cnt;
    return !!cnt;
}
signed main(){
    cin>>n>>k;
    F(i,1,n)cin>>a[i],s[i]=s[i-1]+a[i];
    F(i,1,n)a[i+n]=a[i],s[i+n]=s[i+n-1]+a[i];
    int l=1,r=s[n],best=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(chk(mid))best=mid,l=mid+1;
        else r=mid-1;
    }
    cout<<best<<' '<<res<<'\n';
}

标签:AtCoder,cout,Beginner,nxt,int,cin,i1,补题,mod
From: https://www.cnblogs.com/yhbqwq/p/18402283

相关文章

  • AtCoder Beginner Contest 370
    A-RaiseBothHands(abc370A)题目大意给出Snuke举的左右手情况,如果只举左手,输出Yes,如果只举右手,输出No,否则输出Invalid。解题思路逐一判断即可。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;intmain(void){ios::sync_with......
  • abc370 A-E题解 (AtCoder Beginner Contest 370)
     A这类简单题,看清楚:OutputPrint Yes, No,or Invalid accordingtotheinstructionsintheproblemstatement. B Cstd,这样写(0->n-1,n-1->0),可以少写一个vector1#include<bits/stdc++.h>2usingnamespacestd;34intmain(){5strings,......
  • 题解:Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
    总体情况这次手速够快:ABCin10min,ABCDEin30min。A-RaiseBothHands思路分类讨论很简单的。注意一定要判断\(0,0\)这种情况。Code//Problem:A-RaiseBothHands//Contest:AtCoder-ToyotaProgrammingContest2024#9(AtCoderBeginnerContest370)//URL:......
  • AtCoder Beginner Contest 369
    A-369(abc369A)题目大意给定两个数\(a,b\),问有多少个整数\(x\),使得\(a,b,x\)经过某种排列后成为等差数列,解题思路就三种情况:\(xab\),\(axb\),\(abx\),三种情况都求出来,然后放到set去重即为答案。中间的情况要判断是否是实数。神奇的代码#include<bits/stdc++.h>using......
  • AtCoder Beginner Contest 369 题ABCD详细题解--包含解题思路和两种语言(C++,Python)
    前言:    本文为AtCoderBeginnerContest369题ABCD详细题解,包括题目大意,详细的解题思路和两种语言描述,觉的有帮助或者写的不错可以点个赞几天前的比赛拖的有点久了比赛题目连接:Tasks-AtCoderBeginnerContest369目录题A:题目大意和解题思路:代码(C++):......
  • AtCoder ABC 369题解
    前言本题解部分思路来源于网络,仅供参考!A-369题目大意给定\(A\),\(B\)两个整数,求有多少个整数\(x\)使得可以通过某种排列使得\(A\),\(B\),\(x\)为等差数列。解题思路稍加分析即可得到:如果\(A=B\)则结果为\(1\)。如果\(A=B\)但\((A+B)\bmod......
  • AtCoder Beginner Contest 369 补题记录
    A-369题意:给定A和B,求有多少个x可以和A,B构成等差数列思路:分三种情况讨论A==B则x不得不与A和B想等x位于A和B中间只有B-A为偶数才有这种情况存在x位于A和B两边可以在左边也可以在右边,只要A!=B这种情况总会存在voidsolve(){inta=read(),b=read();......
  • AtCoder Beginner Contest 368(ABC368)
    [ABC369C]CountArithmeticSubarrays题意:判断有多少个区间是等差数列(不能重排)。\(1\len\times10^5\)。思路:赛时看错题了,以为这个区间可以重排,卡了8min,小丑了。首先容易注意到,对于一个区间\([l,r]\),若其是等差数列,则这个区间的子区间\([l',r']\)肯定也是子序列,造成......
  • Atcoder Beginner Contest 369
    AtcoderBeginnerContest369C-CountArithmeticSubarrays题意给出一个长度为\(n\)的序列\(A\),求有多少个\(A\)的连续子序列为等差数列。思路对于递增的右端点,左端点不减。使用双指针,枚举右端点,扫描左端点。时间复杂度:\(O(n)\)。代码#include<bits/stdc++.h>usi......
  • AtCoder Beginner Contest 369 补题记录(A~G)
    AconstintN=1000100;inta[N];signedmain(){intx,y;cin>>x>>y;if(x==y)cout<<"1\n";elseif(x%2==y%2)cout<<"3\n";elsecout<<"2\n";}BconstintN=1000100;inta[N];sign......