2018 ICPC Asia Jakarta Regional Contest(补题中)

 

 

加粗:赛时AC 

普通:赛后AC

我是眼缘,破DP调个半年调不出来,被杰哥重打一遍过了,全程就只出了一题被队友带飞。

A. Edit Distance

题目给的要求很松散,构造的方式有很多。对于0比1多的串我们只要输出一个全是1的串就行,那么我们最少要出现 $ \frac n 2 $以上个0,最少要操作$ \frac n 2 $以上次,1比0多同理。

当0和1相等的时候,我们考察第一个数字,是0输出10000…否则输出0111…

拓展:这题的条件限制的太宽了,最小编辑距离是一种非常典型的DP类问题,思考:假如我们要求构造的序列修改次数最大而不是大于n/2,最后能不能做。我觉得是可以的,感觉这题原本就是这样出的,最后被砍成了签到题,和队友讨论一下然后逮捕

D. Icy Land

一开始没听懂题目。。。

当行和列都大于等于3的时候,中间的每一个都要变成干燥地面,然后外面一圈要除了四角外有一个干燥地面能够进入中间部分。

当行数小于3的时候,要求是除了第一列和最后一列以外,每一列都至少有一个干燥地面,列数小于3的时候也是一样的道理。

当两者都小于3,答案是0.

G. Go Make It Complete

壕哥出的,我一开始想的是二分之类的。

比较暴力的方式是将每一个未连接点对扔进优先队列里,然后从大到小连边,每连一对边就将后面含有这两个点的点对度数+1.记录最小的度数,这种方式复杂度很高。

我们依然从大到小连接,每连接一对点,最多有n-2个其他的点受到影响,对于每个实际受影响的点,如果连接之后度数大于当前记录的答案,那么我们可以直接连接这条边,然后用队列维护被更新的点。用这些点去更新其他的点,那么就可以保证对于每次更新,队列里含有点数小于n个点。如果连接之后小于当前记录的答案,我们就不更新扔回进优先队列里,后续一定是会比队列里更新前的数据优先处理。队列的复杂度上限是O(n3)相当于每个点都做一次,优先队列上限是O(n2logn)相当于每个点对都做一次。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
int v[510][510],v1[510],du[510];
struct node
{
    int sum;
    int i,j;
    friend bool operator <(node a,node b)
    {
        return a.sum<b.sum;
    }
};
priority_queue <node> q;
int main()
{
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for (i=1; i<=m; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        du[x]++,du[y]++;
        v[x][y]=v[y][x]=1;
    }
    for (i=1; i<=n; i++)
        for (j=i+1; j<=n; j++)
            if (!v[i][j])
            {
                node now;
                now.sum=du[i]+du[j];
                now.i=i,now.j=j;
                q.push(now);
            }
    int ans=1e9;
    while(!q.empty())
    {
        node now=q.top();
        q.pop();
        if(v[now.i][now.j])
            continue;
        ans=min(ans,du[now.i]+du[now.j]);
        v[now.i][now.j]=v[now.j][now.i]=1;
        du[now.i]++,du[now.j]++;
        queue<int>q1;
        q1.push(now.i),q1.push(now.j);
        v1[now.i]=v1[now.j]=1;
        //printf("%d %d\n",now.i,now.j);
        while(!q1.empty())
        {
            int i=q1.front();
           // printf("%d\n",i);
            q1.pop();
            v1[i]=0;
            for(j=1; j<=n; j++)
            {
                if (i==j||v[i][j])
                    continue;
                if(du[i]+du[j]<ans)
                {
                    node now;
                    now.sum=du[i]+du[j];
                    now.i=i,now.j=j;
                    q.push(now);
                }
                else
                {
                    v[i][j]=v[j][i]=1;
                    du[i]++,du[j]++;
                    if(!v1[i])
                        q1.push(i),v1[i]=1;
                    if(!v1[j])
                        q1.push(j),v1[j]=1;
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code

I. Lie Detector

签到,跟着题意做就行了。

J. Future Generation

有人血c,有人吸血。

我废了,两个小时调不出来简单dp,最后杰哥重打一遍直接过了。。。

我们预处理出每一个字符串的所有子序列,然后直接对串进行按照字典序排序,dp的时候,对每一个原串它前一个串的子序列串进行二分查找,找到的位置之前的(杰哥是倒过来做的,所以他的代码是之后)所有串都是可以被选中的,所以处理处上一层的最大值,然后用最大值加上当前串的长度即可。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#include <cmath>

using namespace std;
typedef long long ll;
#define finc(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define fdec(i,a,b) for(int i=(int)(b);i-->(int)(a);)
#define reset(a,...) a=decltype(a)(__VA_ARGS__)
#define endl '\n'
#define endb ' '
#define read(a,str) scanf("%"#str,&a)
#define print(a,str) printf("%"#str,a)

int n, m;
vector<string> inp;
vector<vector<string>> sub;
vector<vector<int>> dp;

void f(int now)
{
    int len = inp[now].size();
    finc(i, 1, 1 << len)
    {
        string str;
        finc(j, 0, len)
            if (1 << j & i)
                str += inp[now][j];
        sub[now].push_back(str);
    }
}

void ac()
{
    cin >> n; m = 1 << n;
    reset(inp, n + 1), reset(sub, n + 1), reset(dp, n + 1);
    finc(i, 1, n + 1)
        cin >> inp[i];

    finc(i, 1, n + 1)
        f(i), sort(sub[i].begin(), sub[i].end());

    finc(i, 1, n + 1)
        dp[i] = vector<int>(sub[i].size());

    finc(i, 0, dp[n].size())
        dp[n][i] = sub[n][i].size();
    fdec(i, 0, dp[n].size() - 1)
        dp[n][i] = max(dp[n][i], dp[n][i + 1]);

    fdec(i, 1, n)
    {
        finc(j, 0, sub[i].size())
        {
            auto it = upper_bound(sub[i + 1].begin(), sub[i + 1].end(), sub[i][j]) - sub[i + 1].begin();
            if (it >= sub[i + 1].size())
            {
                dp[i][j] = -1e6;
                continue;
            }
            dp[i][j] = sub[i][j].size() + dp[i + 1][it];
        }
        fdec(j, 0, sub[i].size() - 1)
            dp[i][j] = max(dp[i][j], dp[i][j + 1]);
    }

    if (dp[1][0] <= 0)
        cout << -1 << endl;
    else
        cout << dp[1][0] << endl;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int test = 1; //cin >> test;
    while (test--)
        ac();

    return 0;
}
View Code

K. Boomerangs

队友给我讲懂的。

可以证明,对于一个连通块,偶数个边一定可以全部选定,奇数个边一定只有一个不被选定。

如果每个点都有偶数个边,那很显然。如果有两个奇数个边的点,如果他们相连,那么每个点的边两两相消之后就会剩下这条边,我们可以把这条边消掉,让两个点的某一个剩下一条边和别人相连,当相连的那个点的度为偶数时,这条传递过来的边就被消掉了(此时边总数为偶数),否则就继续传递,只要总边是偶数,一定有方式传递到位使得全都被使用,否则还剩一条边。

基于这个理论,我们跑dfs,在最终节点开始两两相消,如果有剩余的和其父亲节点相消,相当于在dfs树上向上传递,由于dfs序里面包含所有的点,最后这条边一定能够传递到位。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <set>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
struct node
{
    int x, y, z;
};
int v[N], fa[N];
set<int> mp[N],mp1[N];
vector<int>dele;
vector<node> ans;
void dfs(int x)
{
    v[x] = 1;
    for (auto& I : mp[x])
    {
        if (v[I])
            continue;
        fa[I] = x;
        dfs(I);
    }
    int now = 0;
    dele.clear();
    for (auto& I : mp1[x])
    {
        if (I == fa[x])
            continue;
        if (!now)
            now = I;
        else
        {
            ans.push_back({ now,x,I });
            dele.push_back(now);
            dele.push_back(I);
            now = 0;
        }
    }
    if (now&&fa[x])
    {
        ans.push_back({ now,x,fa[x]});
        dele.push_back(now);
        dele.push_back(fa[x]);
    }
    for (int i = 0; i < dele.size(); i++)
    {
        int y = dele[i];
        mp1[x].erase(y);
        mp1[y].erase(x);
    }
}
int main()
{
    int n, m, i;
    scanf("%d%d", &n, &m);
    for (i = 1; i <= m; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        mp[x].insert(y);
        mp[y].insert(x);
        mp1[x].insert(y);
        mp1[y].insert(x);
    }
    for (i = 1; i <= n; i++)
        if (!v[i]) dfs(i);
    printf("%d\n", ans.size());
    for (i = 0; i < ans.size(); i++)
        printf("%d %d %d\n", ans[i].x, ans[i].y, ans[i].z);
    return 0;
}
View Code

L. Binary String

贪心,优先删在最前面的,删到长度相等或者只剩下一个,然后从前往后删后面的,如果相等就一一对比,原数字大就答案+1,否则就输出答案。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#include <cmath>

using namespace std;
typedef long long ll;
#define finc(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define fdec(i,a,b) for(int i=(int)(b);i-->(int)(a);)
#define reset(a,...) a=decltype(a)(__VA_ARGS__)
#define endl '\n'
#define endb ' '
#define read(a,str) scanf("%"#str,&a)
#define print(a,str) printf("%"#str,a)

void ac()
{
    ll n; cin >> n;
    string str; cin >> str;
    bitset<64> inp(str);
    bool flag = 1;
    int ans = 0;
    fdec(i, 0, 64)
    {
        if (inp.to_ullong() <= n)
        {
            cout << ans << endl;
            return;
        }
        if (flag)
        {
            if (i && inp[i])
                if (inp[i - 1] == 1)
                    ans++, inp[i] = 0;
                else
                    flag = 0;
        }
        else
        {
            if (inp[i])
            {
                finc(j, i + 1, 64)
                    inp[j - 1] = inp[j];
                ans++;
            }
        }
    }
    while (inp.to_ullong() > n)
        inp >>= 1, ans++;
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int test = 1; //cin >> test;
    while (test--)
        ac();

    return 0;
}
View Code

 

posted @ 2022-05-11 11:31  ztlsw  阅读(117)  评论(0)    收藏  举报