2022 ICPC 47 沈阳 DCLF

 5题金 4题银铜 3题快铜

ACM生涯第一场ICPC就差点银,开心~

比赛后还得知DRX赢了,冠军皮肤选的千珏,更开心了~

D.DRX vs T1

签到题

题意:

        输入5个字符,D代表DRX赢,T代表T1赢,?代表不知道谁赢。输出可能赢的队伍。

解法:

        统计D和T的数量,若D>=T,那么DRX赢,否则T1赢。因为可以把?全部看作数量多的队伍赢的。

        本人千珏绝活,DRX韩一珏Pyosik粉丝,很希望DRX能赢,这样韩一珏就能选千珏冠军皮肤了。所以写代码的时候特意整活,让判定条件是D>=T,这样D==T的时候,两边都可能赢都是正确答案,输出DRX。(本来想写d+?>t就输出DRX,但是担心出锅,没敢这么写) 打完比赛拿了牌子还得知DRX真夺冠了,超超超开心!!!

核心代码:

char x;
ll d, t;

void Solve(void)
{
    d = t = 0;
    FOR(i, 1, 5)
    {
        cin >> x;
        if (x == 'D')
            ++d;
        if (x == 'T')
            ++t;
    }
    if (d >= t)
        cout << "DRX" << edl;
    else
        cout << "T1" << edl;
    return;
}

C.Clamped Sequence

简单题

题意:

        有n个数,一个限制长度d。选择一段范围[l,r],要求r-l<=d,使得这n个数中,比l小的变为l,比r大的变为r,在范围内的则不变,问n-1个差分的绝对值的和的最大值。

解法:

        首先想到,选择一个范围一定只会让原本数组更劣,不会更优,选择范围是劣化操作。那么希望这个操作能影响的数最少,即让这个范围大小最大,为d。

        之后想到,既然希望影响的数最少,那么一定是更多的数在区间内,就是说,l或者r,一定有一个是卡在数组里的一个数上。

        n只有1e3的数量级,说明可以n^2解,那么直接枚举每一个数作为上限/下限,寻找最优答案。

        因为复杂度够,所以比赛的时候写的很暴力,没有任何优化。

        赛中想到一端要卡住后没仔细想可能另一端卡住更优也要考虑,只一个Check(a[i],a[i]+d)WA了一发,难受,其实复杂度够保险点在多加几组进去也不要紧。

        写完AC那发后因为前一发WA一直不敢交,还好队友*1够果断直接催着交过了。

核心代码:

ll n, d;
ll a[MXN];
ll b[MXN];
ll ans;

void Check(ll l, ll r)
{
    FOR(i, 1, n)
    if (a[i] < l)
        b[i] = l;
    else if (a[i] > r)
        b[i] = r;
    else
        b[i] = a[i];
    ll res = 0;
    FOR(i, 1, n - 1)
    {
        ll tmp = b[i] - b[i + 1];
        if (tmp < 0)
            tmp = -tmp;
        res += tmp;
    }
    ans = max(ans, res);
    return;
}
void Solve(void)
{
    ans = -LNF;
    cin >> n >> d;
    FOR(i, 1, n)
    cin >> a[i];
    FOR(i, 1, n)
    {
        Check(a[i], a[i] + d);
        Check(a[i] - d, a[i]);
    }
    cout << ans << edl;
    return;
}

L.Tavern Chess

模拟题

题意:

         A有n个角色,B有m个角色。角色多的先手,角色数量相同,先手概率一半一半。两边的角色轮流攻击对方,攻击者是当前队伍中攻击过次数最少的,攻击次数相同时取最左的;被攻击者是对面还活着的随机一个,每个活着的被选中概率相等。攻击时,对手会扣自己的攻击力,同时自己会扣对手的攻击力,所以可能平局。扣血后血量会掉,但是攻击力不会掉。血量小于等于0就判定为死亡。问A赢的概率,B赢的概率,平局的概率。

解法:

        用DFS暴力搜索所有情况,即可得到答案。

        赛中因为一直读错题,先是当成最左边的攻击,再是当成攻击力最低的攻击,导致一直过不了样例,以为是代码写太长出BUG了。上百行的代码全删重写来了五六遍,写麻了,才发现是题读错了。(实际上每一遍按照理解错的题目都是对的)

        代码很长,因为懒得用函数统一写,这样写不用脑子写的快。

核心代码:

db aw, bw, dw;
ll n, m;
ll a[MXN];
ll b[MXN];
struct hp
{
    ll aCnt[MXN];
    ll bCnt[MXN];
    ll aHP[MXN];
    ll bHP[MXN];
};

bool Check(hp now, db pp)
{
    ll liveA = 0;
    ll liveB = 0;
    FOR(i, 1, n)
    if (now.aHP[i] > 0)
        ++liveA;
    FOR(i, 1, m)
    if (now.bHP[i] > 0)
        ++liveB;
    if (liveA == 0 && liveB == 0)
    {
        dw += pp;
        return true;
    }
    if (liveA == 0)
    {
        bw += pp;
        return true;
    }
    if (liveB == 0)
    {
        aw += pp;
        return true;
    }
    return false;
}
void DFS(int team, hp now, db p)
{
    if (team == 0)
    {
        ll ATKer;
        ll mn = LNF;
        FOR(i, 1, n)
        if (now.aHP[i] > 0)
            if (now.aCnt[i] < mn)
            {
                ATKer = i;
                mn = now.aCnt[i];
            }
        ++now.aCnt[ATKer];
        ll live = 0;
        FOR(i, 1, m)
        if (now.bHP[i] > 0)
            ++live;
        db p2 = 1.0;
        p2 /= live;
        FOR(i, 1, m)
        {
            if (now.bHP[i] <= 0)
                continue;
            hp nxt = now;
            nxt.aHP[ATKer] -= b[i];
            nxt.bHP[i] -= a[ATKer];
            if (Check(nxt, p * p2) == false)
                DFS(1, nxt, p * p2);
        }
        return;
    }
    if (team == 1)
    {
        ll ATKer;
        ll mn = LNF;
        FOR(i, 1, m)
        if (now.bHP[i] > 0)
            if (now.bCnt[i] < mn)
            {
                ATKer = i;
                mn = now.bCnt[i];
            }
        ++now.bCnt[ATKer];
        ll live = 0;
        FOR(i, 1, n)
        if (now.aHP[i] > 0)
            ++live;
        db p2 = 1.0;
        p2 /= live;
        FOR(i, 1, n)
        {
            if (now.aHP[i] <= 0)
                continue;
            hp nxt = now;
            nxt.bHP[ATKer] -= a[i];
            nxt.aHP[i] -= b[ATKer];
            if (Check(nxt, p * p2) == false)
                DFS(0, nxt, p * p2);
        }
        return;
    }
    return;
}
void Solve(void)
{
    aw = bw = dw = 0.0;
    cin >> n >> m;
    FOR(i, 1, n)
    cin >> a[i];
    FOR(i, 1, m)
    cin >> b[i];
    hp tmp;
    FOR(i, 1, n)
    {
        tmp.aHP[i] = a[i];
        tmp.aCnt[i] = 0;
    }
    FOR(i, 1, m)
    {
        tmp.bHP[i] = b[i];
        tmp.bCnt[i] = 0;
    }
    if (n == m)
    {
        DFS(0, tmp, 0.5);
        DFS(1, tmp, 0.5);
    }
    else if (n > m)
        DFS(0, tmp, 1.0);
    else if (m > n)
        DFS(1, tmp, 1.0);
    cout << SPO(15) << aw << edl;
    cout << SPO(15) << bw << edl;
    cout << SPO(15) << dw << edl;
    return;
}

F.Half Mixed

构造题

题意:

        给出n和m,要求构造一个n×m的01矩阵。定义一个只有0或者只有1的矩阵为纯净矩阵,有0且有1的为混乱矩阵。要求构造的矩阵的所有子矩阵中,纯净矩阵数量和混乱矩阵数量相同。

解法:

        看到数据范围的时候非常亲切,因为前段时间给新生出新生赛的时候就用这种数据恶心过新生。n和m最大都可能是1e6,如果想用数组提前存了再输出就要1e6×1e6共1e12,显然空间是不够的,所以不能用二维数组来存。

        容易想到n*m的矩阵,一共有((n*n+n)/2)* ((m*m+m)/2)个子矩阵。所以这个数一定得是偶数,否则构造不出,直接输出No。

        再然后,想到一列0一列1这样交替,那就有((n*n+n)/2)*m个子矩阵是纯净矩阵。

距离总数1/2还需要修正。于是想到再加列,连续两列1,其他交替,那就多了((n*n+n)/2)个子矩阵。连续三列1,就多了((n*n+n)/2)*(1+2)个。最后统一为,连续k列,可以多((n*n+n)/2)*((k*k+k)/2)个。行同理。

        于是,这道题就转变为了,用1,3,6,10...这种数去构造((m*m+m)/4)-m(或者构造((n*n+n)/4)-n)。

        因为行列很容易弄混,之前第三题L题又一直在反复写代码,写这题的时候已经有点神志不清了。还好队友三人分开想,最后都想到了同一个想法,然后就是我敲代码,队友帮忙处理细节。

核心代码:

ll n, m;
ll a[MXN];
vector<ll> ve;

void Init(void)
{
    a[1] = 1;
    FOR(i, 2, MXN - 3)
    a[i] = a[i - 1] + i;
    return;
}
void Solve(void)
{
    ve.clear();
    cin >> n >> m;
    ll row = (n * n + n) / 2;
    ll col = (m * m + m) / 2;
    ll tot = row * col;
    if (tot & 1)
    {
        cout << "No" << edl;
        return;
    }
    ll f = 0;
    if (col % 2 == 0)
    {
        ll aim = col / 2 - m;
        ll sum = 0;
        ROF(i, m + 1, 1)
        {
            if (aim == 0)
                break;
            ll k = aim / a[i];
            FOR(j, 1, k)
            {
                ve.push_back(i + 1);
                sum += i + 1;
            }
            aim %= a[i];
        }
        f = 1;
        FOR(i, 1, m - sum)
        ve.push_back(1);
    }
    else if (row % 2 == 0)
    {
        ll aim = row / 2 - n;
        ll sum = 0;
        ROF(i, n + 1, 1)
        {
            if (aim == 0)
                break;
            ll k = aim / a[i];
            FOR(j, 1, k)
            {
                ve.push_back(i + 1);
                sum += (i + 1);
            }
            aim %= a[i];
        }
        f = 2;
        FOR(i, 1, n - sum)
        ve.push_back(1);
    }
    if (f == 0)
    {
        cout << "No" << edl;
        return;
    }
    cout << "Yes" << edl;
    if (f == 1)
    {
        FOR(i, 1, n)
        {
            bool flag = true;
            ll ch = 1;
            for (auto it : ve)
            {
                FOR(j, 1, it)
                {
                    if (flag)
                        flag = false;
                    else
                        cout << ' ';
                    cout << ch;
                }
                ch = 1 - ch;
            }
            cout << edl;
        }
    }
    if (f == 2)
    {
        ll ch = 1;
        for (auto it : ve)
        {
            FOR(i, 1, it)
            {
                bool flag = true;
                FOR(j, 1, m)
                {
                    if (flag)
                        flag = false;
                    else
                        cout << ' ';
                    cout << ch;
                }
                cout << edl;
            }
            ch = 1 - ch;
        }
    }
}
int main(void)
{
    std::ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    // #ifdef cincoutcmd
    //  freopen("cin.txt", "r", stdin);
    //  freopen("cout.txt", "w", stdout);
    // #endif
    Init();
    int t;
    cin >> t;
    while (t--)
        Solve();
    return 0;
}

这场L题读错题太伤了,浪费了几个小时的罚时,而且很搞心态。导致后面F时间也不多,慌忙交题白wa了好几发(甚至因为赶时间最开始输出格式都是错的)。不读错题应该能直接ACM生涯首银,有点可惜。下个目标---首银!

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值