AtCoder Beginner Contest 285(D-G)

22 篇文章 1 订阅
21 篇文章 0 订阅

D - Change Usernames (atcoder.jp)

        (1)题目大意        

                每个用户都要改变自己的名称从A-B,但是他们不想自己改的名称被别人用掉了,因此让你判断是否可以改。

         (2)解题思路

                这种类似于匹配的问题,可以想到并查集,我们可以先hash每个名称,然后判断是否有两个名称出现在同一集合即可。

         (3)代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define ll long long
#define db double
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 2e5 + 10;
unordered_map<string,int> mp;
int cnt;
struct UFS {
    int f[N],siz[N];
    void init(int n) {for(int i=1;i<=n;i++)siz[i]=1,f[i]=i;}
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    bool same(int x,int y){return find(x)==find(y);}
    void merge(int x,int y) {if(!same(x,y))siz[find(y)]+=siz[find(x)],f[find(x)]=find(y);}
    int qsz(int x){return siz[find(x)];}
}comb;
int get(string s)
{
    if(!mp.count(s)) mp[s]=++cnt;
    return mp[s];
}
void solve()
{
    int n;
    cin>>n;
    comb.init(2*n);
    bool ok=true;
    rep(i,1,n) {
        string s1,s2;
        cin>>s1>>s2;
        int x=get(s1),y=get(s2);
        if(comb.same(x,y)) ok=false;
        else comb.merge(x,y); 
    }
    if(ok) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

E - Work or Rest (atcoder.jp)

        (1)题目大意

        现在有n天,若当天放假生产力则为0,否则为a[i],定义两个假期之间的生产效率为,其中n为距离\sum^{n}_{i=1}A[\frac{i+1}{2}]

         (2)解题思路

                我们考虑dp[i][j]表示当前是第i天,已经连续工作了j天的效率最大为多少。

                那么就有dp[i][j] = max(dp[i][j],dp[i-1][j-1])表示当前为工作日

                dp[i][0]=max(dp[i-1][j-1]+s[j-1],dp[i][0])表示当前为假期,前面已经有j-1天工作了的效率。

                注意最后一天为假期我们的dp值是没有更新的,因此我们取答案的时候要加上s[i]。

        (3)代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define ll long long
#define db double
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 5010;
ll s[N],dp[N][N],a[N];
void solve()
{
    int n;
    cin>>n;
    rep(i,1,n) cin>>a[i],s[i]=s[i-1]+(a[(i+1)>>1]);
    rep(i,1,n) rep(j,1,i) {
        dp[i][j]=max(dp[i-1][j-1],dp[i][j]);
        dp[i][0]=max(dp[i-1][j-1]+s[j-1],dp[i][0]);
    }
    ll ans=0;
    rep(i,1,n) ans=max(ans,dp[n][i]+s[i-1]);
    cout<<ans<<endl; 
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

F - Substring of Sorted String (atcoder.jp)

        (1)题目大意

                给你一个长度为N的串S,第一个操作是把第x个字符换成c,第二个操作是询问[l,r]这个串是否为原串排序后的子串。

         (2)解题思路

                考虑若[l,r]串若能成为原串排序后子串,有以下两个条件。

                        1.[l,r]的逆序对数量为0

                        2.[l,r]除了首尾字母的数量可以少于原串,中间的必须要和原串相同。

                我们可以想到第一个条件可以用逆序对的方式用树状数组处理出来。

                第二个条件我们也可以开26个树状数组统计。

        (3)代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define ll long long
#define db double
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
#define lowbit(x) x&-x
using namespace std;
const int N = 2e5 + 10;
char s[N];
int seg[28][N],cnt[26],n;
void add(int p,int x,int v)
{
    while(x<=n) {
        seg[p][x] += v;
        x += lowbit(x);
    }
}
int qry(int p,int x)
{
    int res=0;
    while(x>=1) {
        res += seg[p][x];
        x -= lowbit(x);
    }
    return res;
}
void solve()
{
    scanf("%d%s",&n,s+1); 
    rep(i,1,n) {
        add(s[i]-'a',i,1);
        if(i>1&&s[i]<s[i-1]) add(27,i,1); 
    }
    int q;
    char ch;
    scanf("%d",&q);
    while(q--) {
        int op,l,r;
        scanf("%d",&op);
        if(op==1) {
            scanf("%d %c",&l,&ch);
            char vm = s[l];
            if(l>1&&s[l]<s[l-1]) add(27,l,-1);
            if(l<n&&s[l]>s[l+1]) add(27,l+1,-1);
            s[l] = ch;
            if(l>1&&s[l]<s[l-1]) add(27,l,1);
            if(l<n&&s[l]>s[l+1]) add(27,l+1,1);
            add(vm-'a',l,-1);add(s[l]-'a',l,1);
        }
        else {
            scanf("%d%d",&l,&r);
            if(qry(27,r)-qry(27,l)) puts("No");
            else {
                bool ok=true;
                rep(j,s[l]-'a'+1,s[r]-'a'-1) {
                    cnt[j]=qry(j,r)-qry(j,l-1);
                    if(cnt[j]!=qry(j,n)) {
                        ok=false;
                        break;
                    }
                }
                if(ok) puts("Yes");
                else puts("No");
            }
        }
    }
}
int main()
{
    int T = 1;
    while(T --) solve();
    return 0;
}

G - Tatami (atcoder.jp)

        (1)题目大意

                给你一个W*H的二维矩阵,对于每个1,必须有1个1*1的方块覆盖,每个2,必须由1*2或者2*1的方块覆盖,?可以被1*1或者1*2或者2*1的方块覆盖,问最后能否使这个矩阵被完全覆盖。

         (2)解题思路

                很容易想到1在这个矩阵没啥限制,因此不用管1,只需要管2和?的匹配,由于每一个2和?只能被使用一次,又是一个匹配问题,因此我们考虑最大流拆点。

                把每个2和问号拆成入点和出点。

                若当前为2,则源点向当前点的入点加一条容量为1的管道,并从当前点的出点向汇点加一条容量为1的管道,并且向四个方向不为1的点用出点向他们的入点连一条边。

                若当前为?,则直接向汇点连一条容量为1的管道。

        (3)代码实现

#include <bits/stdc++.h>
#define ll long long
const int inf = 0x3f3f3f3f;
//要保证源点和汇点联通
using T = long long;
struct HLPP {
    struct Edge {
        int j, q;
        T x;
    };
    int N, K = 0, m = 0, cnt = 0;
    std::vector<std::vector<Edge>> G;
    std::vector<int> H, p1;
    std::vector<T> X;
    std::vector<std::vector<int>> Q;
    HLPP(int nn) : N(nn), G(N + 1), H(N + 1), p1(N + 1), X(N + 1), Q(N * 2 + 2) {}
    void add(int i, int j, T x, T y = 0) {
        int p = G[i].size(), q = G[j].size();
        G[i].push_back({j, q, x});
        G[j].push_back({i, p, y});
    }
    void push(int i) {
        Q[H[i]].push_back(i);
        m = std::max(m, H[i]);
    }
    void relabel(int t) {
        cnt = 0;
        for (auto &q : Q) q.clear();
        fill(H.begin(), H.end(), N * 2 + 1);
        std::vector<int> Q(N);
        int s = -1, e = -1;
        H[Q[++e] = t] = 0;
        while (s < e) {
            int i = Q[++s], h = H[i] + 1;
            for (auto &[j, q, x] : G[i])
                if (G[j][q].x && h < H[j]) {
                    H[Q[++e] = j] = h;
                    if (X[j] > 0) push(j);
                }
        }
    }
    void discharge(int i) {
        auto &v = X[i];
        int h = N * 2;
        for (int &p = p1[i], m = G[i].size(); m--; p = (p ? : G[i].size()) - 1) {
            auto &[j, q, x] = G[i][p];
            if (!x) continue;
            if (H[i] != H[j] + 1) {
                h = std::min(h, H[j] + 1);
                continue;
            }
            auto f = std::min(x, v);
            x -= f, v -= f;
            if (!X[j]) push(j);
            X[j] += f, G[j][q].x += f;
            if (!v) return;
        }
        cnt++;
        H[i] = h;
        if (H[i] < N && X[i] > 0) push(i);
    }
    T max_flow(int s, int t, T inf = std::numeric_limits<T>::max()) {
        add(s, t, 1);relabel(t);push(s);
        X[s] = inf, X[t] = -inf;
        for (; ~m; m--)
            while (Q[m].size()) {
                int i = Q[m].back();
                Q[m].pop_back();
                if (H[i] == m) discharge(i);
                if (cnt >= 4 * N) relabel(t);
            }
        return X[t] + inf - 1;
    }
};
char mp[310][310];
int n,m,mx[4]={1,-1,0,0},my[4]={0,0,1,-1};
int get(int i,int j,int t)
{
    return t*n*m + i*m+j;
}
int main()
{
    scanf("%d%d",&n,&m);
    HLPP f(2*n*m+10);
    for(int i=0;i<n;i++) scanf("%s",mp[i]);
    int tot=0,S=2*n*m+1,T=2*n*m+2;
    for(int i=0;i<n;i++) {
        for(int j=0;j<m;j++) {
            if(mp[i][j]=='1') continue;
            if(mp[i][j]=='2'){
                tot++;
                f.add(S,get(i,j,0),1);
                for(int k=0;k<4;k++) {
                    int dx=i+mx[k],dy=j+my[k];
                    if(dx<0||dx>=n||dy<0||dy>=m) continue;
                    if(mp[dx][dy]=='1') continue;
                    f.add(get(i,j,0),get(dx,dy,1),1);
                }
            }
            f.add(get(i,j,1),T,1);
        }
    }
    if(tot==f.max_flow(S,T)) puts("Yes");
    else puts("No");
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值