寒假训练第一天-Codeforces Round #501 (Div. 3)

寒假训练第一天-Codeforces Round #501 (Div. 3)

前言:又到寒假了,依旧是被疫情困在家里的一天,还是希望自己不那么fw,和队长约了一块训练,div3开始,先练练手速,毕竟忙着期末考试得有半个月没怎么碰过键盘了,该账号开学前会持续更新每日训练题解,寒假目标:紫名。

题目链接

A-Points in Segments

题意:一个区间长为m,n 次询问,每次询问标记一段区间,问最后有多少区间没有标记。该题可参考校门外的树

题解:数据较小,暴力标记即可,时间复杂度O(nm),下面直接粘代码

bool book[110];
int32_t main()
{
    
    
    int n, m, a, b, num = 0;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
    
    
        cin >> a >> b;
        for(int j = a; j <= b; j++)
            book[j] = 1;
    }
    for(int i = 1; i <= m; i++)
        if(book[i] == 0) num++;
    cout << num << endl;
    for(int i = 1; i <= m; i++)
    {
    
    
        if(book[i] == 0) cout << i << ' ';
    }
    return 0;
}

再粘一下差分的代码(O(max(n, m)))

int a[110], s[110];
int32_t main()
{
    
    
    ICO;
    int n, m, l, r, num = 0;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
    
    
        cin >> l >> r;
        a[l]++, a[r + 1]--;
    }
    for(int i = 1; i <= m; i++)
    {
    
    
        s[i] = s[i - 1] + a[i];
        if(s[i] == 0) num++;
    }
    cout << num << endl;
    for(int i = 1; i <= m; i++)
        if(s[i] == 0) cout << i << ' ';
    return 0;
}

B-Obtaining the String

题意:给你两个字符串s,t,移动s的字母(只能相邻之间移动),移动多少次可使s成为t(不要求最小移动步数),题目限制移动步数不得超过e4。

题解:很明显如果s如果能通过移动成为t,最大步数不会超过n^2,e4的限制就没有意义了,题目不要求最少步数,即可通过暴力向后匹配未匹配到的s[i],然后将匹配的字母交换到前面即可。

int a[30], b[30];
vector<int> res;
int32_t main()
{
    
    
    ICO;
    int n, book = 0;
    string s, t;
    cin >> n >> s >> t;
    for(int i = 0; i < n; i++)  	//记录包含字母的个数
    {
    
    
        a[s[i] - 'a']++;
        b[t[i] - 'a']++;
    }
    for(int i = 0; i < 26; i++)		//若包含字母不同,肯定不能匹配
    {
    
    
        if(a[i] != b[i])
        {
    
    
            cout << -1;
            return 0;
        }
    }
    for(int i = 0; i < n; i++)
    {
    
    
        if(s[i] != t[i])		//遇到不匹配的字母
        {
    
    
            int j = i + 1;
            while(s[j] != t[i]) j++;	//两字符串所含字母相同,则一定可以找到匹配的字母
            j--;
            while(j >= i)		//将匹配到的字母交换到前面,并记录交换路径
            {
    
    
                swap(s[j], s[j + 1]);
                res.push_back(j + 1);
                j--;
            }
        }
    }
    cout << res.size() << endl;
    for(int i = 0; i < res.size(); i++)
        cout << res[i] << ' ';
    return 0;
}

C-Songs Compression

题意:有n个文件,内存限制为m。每个文件可压缩,现给你每个文件压缩前和压缩后的内存,要压缩一部分文件使所有的文件可以全部存储,求最少压缩文件的数量。

题解:用数组存储一下压缩文件的内存差,然后排序,从差值最大的开始压缩,直到可以全部存储,注意数据范围。

ll a[maxn], b[maxn], c[maxn];
int32_t main()
{
    
    
    ICO;
    ll n, m, res = 0;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
    
    
        cin >> a[i] >> b[i];
        c[i] = a[i] - b[i];
        res += a[i];
    }
    sort(c + 1, c + 1 + n, greater<ll> ());
    int num = 0;
    for(int i = 1; i <= n; i++)
    {
    
    
        if(res <= m) break;
        res -= c[i], num++;
    }
    if(res <= m) cout << num;
    else cout << -1;
    return 0;
}

D-Walking Between Houses

题意:有n个房子(编号1-n),你需要走k步然后走过的价值和为s,价值是你每走一步的起点和终点所在房间编号的差的绝对值,两步之间不能处于同一房子内(价值为0),所以一步的价值∈[1, n - 1],输出1-k步自己所在房间的编号。

题解:这题是这场比赛我被卡时间最长的,读完题发现题意清楚,思路也有,但是上手敲的时候发现其中有了诸多限制,比如你走i步就必须留下k - i的距离(要保证接下来的k - i步都有房子可走),最后所剩距离不够一趟的时候的处理方法等等。说一下思路,在你保留k - i的时候,若所剩距离大于n - 1,则可以一步从头到尾(从尾到头),否则就走完该步可走的,最后的k - i步每步走1就好。

int32_t main()
{
    
    
    ICO;
    ll n, k, s;
    cin >> n >> k >> s;
    if((n - 1) * k < s || k > s) cout << "NO" << endl;
    else
    {
    
    
        cout << "YES" << endl;
        ll p, sum = 0, now = 1;
        bool ok = 1;
        for(int i = 1; i <= k; i++)
        {
    
    
            p = (s - sum) - (k - i);	//p为当前可走价值  sum为已走价值
            if(p == 1)		//最后k - i步,每步走1,注意转头
            {
    
    
                int j = i;
                while(j <= k)
                {
    
    
                    while(j <= k && (++now) <= n)
                    {
    
    
                        cout << now << ' ';
                        j++;
                    }
                    now--;
                    while(j <= k && (--now) >= 1)
                    {
    
    
                        cout << now << ' ';
                        j++;
                    }
                    now++;
                }
                break;
            }
            if(p >= n)		//可走价值大于一步最大价值时头尾跳即可
            {
    
    
                if(ok) now = n;
                else now = 1;
                p = n - 1;
                ok = !ok;
            }
            else
            {
    
    
                if(ok) now = 1 + p;
                else now = n - p;
            }
            sum += p;
            cout << now << ' ';
        }
    }
    return 0;
}

E-Stars Drawing

题意:判断所给矩阵是否可以拆成几个单独的星星,原矩阵的‘*’可被拆成的星星共用,即被拆成的完整的星星的并集等于原矩阵。

题解:使用二维差分,对所拆的星星进行覆盖,看覆盖后的图案和原矩阵是否匹配。

const int maxn = 1e3 + 2;
char ch[maxn][maxn];
int l[maxn][maxn], r[maxn][maxn], up[maxn][maxn], down[maxn][maxn], res1[maxn][maxn], res2[maxn][maxn];
int res[maxn][maxn];
vector<pair<pair<int, int>, int> > v;
int32_t main()
{
    
    
    ICO;
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
    
    
        for(int j = 1; j <= m; j++)
        {
    
    
            cin >> ch[i][j];
            if(ch[i][j] == '*')
            {
    
    
                if(ch[i][j - 1] == '*') l[i][j] = l[i][j - 1] + 1;
                if(ch[i - 1][j] == '*') up[i][j] = up[i - 1][j] + 1;
            }

        }
    }
    for(int i = n; i >= 1;i--)
    {
    
    
        for(int j = m; j >= 1; j--)
        {
    
    
            if(ch[i][j] == '*')
            {
    
    
                if(ch[i + 1][j] == '*') down[i][j] = down[i + 1][j] + 1;
                if(ch[i][j + 1] == '*') r[i][j] = r[i][j + 1] + 1;
            }
        }
    }
    for(int i = 1; i <= n; i++)
    {
    
    
        for(int j = 1; j <= m; j++)
        {
    
    
            int minn = min(min(l[i][j], up[i][j]), min(r[i][j], down[i][j]));
            if(minn)
            {
    
    
                pair<int, int> l;
                pair<pair<int, int>, int> q;
                l.first = i, l.second = j;
                q.first = l, q.second = minn;
                v.push_back(q);
                res1[i][j - minn]++, res1[i][j + minn + 1]--;
                res2[j][i - minn]++, res2[j][i + minn + 1]--;
            }
        }
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            res1[i][j] += res1[i][j - 1];
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++)
            res2[i][j] += res2[i][j - 1];
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            res[i][j] = res1[i][j] + res2[j][i];
    for(int i = 1; i <= n; i++)
    {
    
    
        for(int j = 1; j <= m; j++)
        {
    
    
            if(ch[i][j] == '*' && res[i][j] == 0)
            {
    
    
                cout << -1 << endl;
                return 0;
            }
        }
    }
    int l = v.size();
    cout << l << endl;
    for(int i = 0; i < l; i++)
        cout << v[i].first.first << ' ' << v[i].first.second << ' ' << v[i].second << endl;
    return 0;
}

总结:长时间不敲代码确实会生疏很多,D题当时被卡了太久,期待在以后的学习中提高自己。

猜你喜欢

转载自blog.csdn.net/Siyue1999/article/details/112504995