返回顶部

AtCoder Beginner Contest 213 (A~F)

A - Bitwise Exclusive Or

  • 水题

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int a,b;
        cin>>a>>b;
        cout<<(a^b)<<'\n';
    
    
        return 0;
    }
    
    

B - Booby Prize

  • 水题

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int n;
        cin>>n;
        vector<PII> a(n+1);
        rep(i,1,n){
            cin>>a[i].fi;
            a[i].se=i; 
        }
    
        sort(a.begin()+1,a.end());
        cout<<a[n-1].se<<'\n';
    
    
        return 0;
    }
    
    

C - Reorder Cards

  • 题意:给你一个\(n\)x\(m\)的空矩阵,将矩阵的\(n\)个点标记,然后删除所有没有任何标记的整行和整列,问原标记的点在新矩阵中的位置.

  • 题解:将标记的点的坐标存到两个vector中然后离散化直接输出下标即可.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int h,w,n;
    int a[N],b[N];
    unordered_map<int,int> mp1,mp2;
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>h>>w>>n;
        vector<int> row,col;
        rep(i,1,n){
            cin>>a[i]>>b[i];
            if(!mp1[a[i]])
            row.pb(a[i]);
            if(!mp2[b[i]])
            col.pb(b[i]);
            mp1[a[i]]++;
            mp2[b[i]]++;
        }
        sort(row.begin(),row.end());
        sort(col.begin(),col.end());
        rep(i,1,n){
            int x=upper_bound(row.begin(),row.end(),a[i])-row.begin();
            int y=upper_bound(col.begin(),col.end(),b[i])-col.begin();
            cout<<x<<' '<<y<<'\n';
        }
    
    
        return 0;
    }
    
    

D - Takahashi Tour

  • 题意:有\(n\)个点,\(n-1\)条边,从第一个点\(1\)开始走,每次选择没有访问的最小的点走,如果出边均访问过,则返回上一个点,直到返回到\(1\)且无路可走时停止,按顺序输出走过的路径。

  • 题解:直接dfs模拟即可。

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n;
    int u,v;
    vector<int> edge[N];
    bool vis[N];
    vector<int> ans;
    
    void dfs(int u){
        ans.pb(u);
        vis[u]=true;
        for(auto to:edge[u]){
            if(!vis[to]){
                dfs(to);
                ans.pb(u);
            }
        }
        if(u==1) return;
    }
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>n;
        rep(i,1,n-1){
            cin>>u>>v;
            edge[u].pb(v);
            edge[v].pb(u);
        }
        rep(i,1,n){
            sort(edge[i].begin(),edge[i].end());
        }
        dfs(1);
        for(auto w:ans) cout<<w<<' ';
        return 0;
    }
    
    

E - Stronger Takahashi

  • 题意:有\(n\)x\(m\)的网格图,#表示不能走,.表示能走,初始位置在\((1,1)\),每次可以向下或者向右走,你可以使用\(1\)点力量使得图中任意位置的一个\(2\)x\(2\)的子矩阵变为\(.\),问你最少使用多少能量才能走到右下角\((n,m)\).

  • 题解:使用01-BFS,首先上下左右四个方向,如果能走,也就代表没有花费,那么可以看成边权为\(0\),然后更新距离,push_front(),再看周围相邻的\(2\)x\(2\)的矩阵,一共\(20\)个方向,我们花费\(1\)个贡献,这些点都能走到,那么就看成边权是\(1\),更新距离再push_back()即可.

  • 代码:

    #include <iostream>
    #include <iomanip>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <vector>
    #include <map>
    #include <set>
    #include <unordered_set>
    #include <unordered_map>
    #define ll long long
    #define db double
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int lcm(int a,int b){return a/gcd(a,b)*b;}
    
    int n,m;
    char s[505][505];
    bool vis[505][505];
    int dis[505][505];
    int f_dx[]={-1,0,1,0},f_dy[]={0,1,0,-1};
    int c_dx[]={-1,-2,-1,-2,0,0,1,2,1,2,1,2,0,0,-1,-2,-1,1,1,-1},c_dy[]={0,0,1,1,1,2,1,1,0,0,-1,-1,-1,-2,-1,-1,2,2,-2,-2};
    deque<PII> dq;
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        #ifdef lr001
            freopen("/Users/somnus/Desktop/data/in.txt","r",stdin);
        #endif
        #ifdef lr002
            freopen("/Users/somnus/Desktop/data/out.txt","w",stdout);
        #endif
        cin>>n>>m;
        me(dis,INF,sizeof(dis));
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                cin>>s[i][j];
            }
        }
        dq.push_back({1,1});
        dis[1][1]=0;
        while(!dq.empty()){
            auto tmp=dq.front();
            dq.pop_front();
            int x=tmp.fi,y=tmp.se;
            if(vis[x][y]) continue;
            vis[x][y]=true;
            for(int i=0;i<4;++i){
                int tx=x+f_dx[i];
                int ty=y+f_dy[i];
                if(tx>=1 && tx<=n && ty>=1 && ty<=m && s[tx][ty]=='.' && dis[tx][ty]>dis[x][y]){
                    dis[tx][ty]=dis[x][y];
                    dq.push_front({tx,ty});
                }
            }
            for(int i=0;i<20;++i){
                int tx=x+c_dx[i];
                int ty=y+c_dy[i];
                if(tx>=1 && tx<=n && ty>=1 && ty<=m && dis[tx][ty]>dis[x][y]+1){
                    dis[tx][ty]=dis[x][y]+1;
                    dq.push_back({tx,ty});
                }
            }
        }
        cout<<dis[n][m]<<'\n';
        return 0;
    }
    
    

F - Common Prefixes

  • 题意:有一长度为\(n\)的字符串\(S\),\(S_i\)表示从\(i\)\(n\)的后缀\(S_{i...n}\),\(f(S_i,S_j)\)表示后缀\(S_i\)\(S_j\)的最长公共前缀,求对于每个\(k=1,2,...,n\),\(Sum_k=f(S_k,S_1)+f(S_k,S_2)+...+f(S_k,S_n)\)的值。

  • 题解:先用后缀数组处理出\(sa\)\(height\)数组,这个不难,关键是我们要怎么在\(O(n)\)的时间内求出\(\sum_{j=1}^{n}min^{max(i,j)}_{k=min(i,j)}height_k\).

    看排序后的后缀,对于\(i<j\),有\(f(S_{sa_i},S_{sa_j})=min(f(S_{sa_i},S_{sa_i+1}),...,f(S_{sa_{j-1}},S_{sa_{j}}))\).

    \(g(i,j)=min(height_{i+1},...,height_j)\).注意这里的\(i\)\(j\)代表的是排名.

    \(D_k=g(1,k)+g(2,k)+...+g(k-1,k)\).\(G_k=g(k,k+1)+...+g(k,n)\).

    我们的答案就是\(\sum^{n}_{k=1}(D_k+G_k)\),对\(D_k\)进行分析.

    \[D_k=g(1,k)+...+g(k-1,k) \\ D_{k+1}=g(1,k+1)+...+g(k-1,k+1)+g(k,k+1) \\ =min(g(1,k),height_{k+1})+...+min(g(k-1,k),height_{k+1})+height_{k+1} \]

    因为每次造成贡献改变之和当前的\(height_k\)有关,而且我们是从\(1\)按排名线性遍历的,所以会一直向前改变贡献,那么这个操作我们可以用单调栈维护。可能有点抽象,具体看代码。

    从排名第二的后缀遍历,每次得到的是\(D_i=g(1,i)+g(2,i)+...+g(i-1,i)\)的贡献,我们可以把\(D_i\)看成一段一段的,每段的\(g\)的值不同,但一定是非严格递增的,因为如果后面某一段的\(lcp\)比前面小,因为\(lcp\)取连续最小值的性质,前面后缀的\(lcp\)值一定不会大于后面的,这样理解了话,那么就用单调栈动态维护贡献然后加给原后缀编号的答案数组就好了。

    \(G_k\)同理。

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n,m;
    string s;
    int sa[N],x[N],y[N],c[N],rk[N],height[N];
    
    void get_sa(){
        for(int i=1;i<=n;++i) c[x[i]=s[i]]++;
        for(int i=2;i<=m;++i) c[i]+=c[i-1];
        for(int i=n;i;i--) sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1){
            int num=0;
            for(int i=n-k+1;i<=n;++i) y[++num]=i;
            for(int i=1;i<=n;++i){
                if(sa[i]>k) y[++num]=sa[i]-k;
            }
            for(int i=1;i<=m;++i) c[i]=0;
            for(int i=1;i<=n;++i) c[x[i]]++;
            for(int i=2;i<=m;++i) c[i]+=c[i-1];
            for(int i=n;i;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1,num=1;
            for(int i=2;i<=n;++i){
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
            }
            if(num==n) break;
            m=num;
        }
    }
    
    void get_height(){
        for(int i=1;i<=n;++i) rk[sa[i]]=i;
        for(int i=1,k=0;i<=n;++i){
            if(rk[i]==1) continue;
            if(k) k--;
            int j=sa[rk[i]-1];
            while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++;
            height[rk[i]]=k;
        }
    }
    int stk[N],top;
    ll ans[N];
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>n;
        cin>>s;
        m=122;
        s=" "+s;
        get_sa();
        get_height();
    
        stk[0]=1;
        ll res=0;
        for(int i=2;i<=n;++i){
            while(top && height[stk[top]]>=height[i]){
                res-=1ll*(stk[top]-stk[top-1])*height[stk[top]];
                top--;
            }
            res+=1ll*(i-stk[top])*height[i],stk[++top]=i;
            ans[sa[i]]+=res;
        }
        res=0;
        top=0;
        stk[0]=n+1;
        for(int i=n;i>=1;--i){
            ans[sa[i]]+=res+n-sa[i]+1;
            if(i==1) continue;
            while(top && height[stk[top]]>=height[i]){
                res-=1ll*(stk[top-1]-stk[top])*height[stk[top]];
                top--;
            }
            res+=1ll*(stk[top]-i)*height[i],stk[++top]=i;
        }
    
        for(int i=1;i<=n;++i) cout<<ans[i]<<'\n';
    
        return 0;
    }
    
    
posted @ 2021-08-09 22:14  Rayotaku  阅读(128)  评论(0编辑  收藏  举报