2025杭电暑期(8) 最努力的活着 推式子

赛后11s AC 好恨啊!

我们可以简单的发现,每一轮都把剩余的所有人的价值,加入到总价值里,并且每一轮都会淘汰几人
越先淘汰的人,所作出的贡献越少,反过来想,越先淘汰的人,做出的负贡献就越多

所以,可以给每一轮淘汰的人赋一个当前最小的编号,保证大编号可以更多次的被加入到贡献之中
自然而然的,暴力就打出来了,但是数据范围太大了,怎么办???

\(假设n=15,w=4;\)
\(第一轮会选出3人,当前n=12\)
\(第二轮会选出3人,当前n=9\)
\(第三轮会选出2人,当前n=7\)
\(第四轮会选出1人,当前n=6\)
\(第五轮会选出1人,当前n=5\)
\(第六轮会选出1人,当前n=4\)
\(第七轮会选出1人,当前n=3.结束。\)

\(有观察到什么吗?可能会有很多轮,选出的人数是相同的!这就可以考虑数论分块了!\)
\(设当前人数为:now\)
\(则当前轮次,一轮淘汰cnt = now/w人\)
\(人数剩余sum人的时候,一轮淘汰cnt-1人\)
\(于是我们要计算出sum,可以简单的发现,sum = cnt*w-1\)
\(于是我们就可以确定,每轮取cnt人,这样的轮次有lun = max((now-sum)/cnt,1);轮\)

得到cnt和lun之后,我们当然想要一次算出这些的答案,毕竟这些很有规律!
设初始值p=1,表示当前填的被淘汰人的编号
还是以n=15,w=4为例子,一开始我们淘汰3人,一共两轮,当然先填上1,2,3
之后淘汰3人,填上4,5,6
可以发现,这样子所产生的负贡献为:

\(2*(1+2+3)+1*(4+5+6)\)
$ 化简一下:$
\(3*(1+2+3)+1*(3+3+3)\)

再造一个数据呢?
假设一开始淘汰3人,一共3轮,就可以得出这样的式子:

\(3*(1+2+3)+2*(4+5+6)+1*(7+8+9)\)
\(6*(1+2+3)+3*(0*(3+3+3))+2*(1*(3+3+3))+1*(2*(3+3+3))\)
\(6*(1+2+3)+4*(3+3+3)\)

于是就可以得到:
\((((1+1+lun)*lun)/2) * ((p+p+cnt)*cnt/2) + ((\sum_{i=1}^{lun-1} (lun-i)*i)*(cnt*cnt))\)

\[\frac{(2 + \text{lun}) \cdot \text{lun}}{2} \cdot \frac{(2p + \text{cnt}) \cdot \text{cnt}}{2} + \left( \frac{\text{lun}^2 (\text{lun} - 1)}{2} - \frac{\text{lun} (\text{lun} - 1)(2\text{lun} - 1)}{6} \right) \cdot \text{cnt}^2 \]

最后的\((cnt * cnt)\)对应了\((3+3+3)\)

这下,我们把\(p\)\(p+(cnt*lun)\)的数字都填完了,记一下当前填的数字的总和
之后算\(cnt-1\)的数据的时候再乘上经过多少轮

然后我们将\(now -=lun*cnt\),已经走了这么多人了…
再次进入下一轮循环,直到\(now<w\)
特判\(w==1\)时,直接输出总值

按理说cnt很大的时候会超时的,但是想不到其他办法了,结果跑的还挺快,70多ms就没了
AC代码(写的比较乱,建议自己写)

#include<bits/stdc++.h>
#define ffp(x,y,z) for(ll (x) = (y);(x)<=(z);(x++))
#define ffs(x,y,z) for(ll (x) = (y);(x)>=(z);(x--))
#define pii pair<ll ,ll> 
#define ll __int128
#define q_ (qd())
using namespace std;
const double ex = 1e-7;
const int iINF = 0x3f3f3f3f;
const ll lINF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 1000000000+7;
ll qd() {
    ll w = 1, c, ret;
    while ((c = getchar()) > '9' || c < '0')
        w = (c == '-' ? -1 : 1); ret = c - '0';
    while ((c = getchar()) >= '0' && c <= '9')
        ret = ret * 10 + c - '0';
    return ret * w;
}
int stime()
{
    timeb ti;
    static bool f = 1;
    ftime(&ti);
    while (1)
    {
        if (f) { srand(ti.millitm * 117); f = 0; }

        int temp = rand();
        if (temp) { return temp > 0 ? temp : -temp; }
    }
}
ll gcd(ll a, ll b)
{
    if (a == 0)return b;
    return a % b == 0 ? b : gcd(b, a % b);
}
ll qs(ll a, ll b)
{
    ll bei = a;
    a = 1;
    while (b)
    {
        if (b & 1) { a = a * bei % MOD; }
        bei = bei * bei % MOD;
        b >>= 1;
    }
    return a;
}
ll inv(ll a)
{
    return qs(a, MOD - 2);
}

void wit(ll x)
{
    if (x < 10)putchar('0' + x);
    else {
        wit(x / 10);
        putchar('0' + x % 10);
    }
}
void solve()
{
    ll n = q_;
    ll w = q_;

    ll now = n;
    ll ans = 0;
    ll jian = 0;
    ll p = 1;
    ll sumlun = 0;
    auto L = [&](ll p, ll lon)->ll
        {
            return (p + p + lon - 1) * lon / 2;
        };
    vector<ll>tcnt, tlun,id;
    if (w == 1)
    {
        wit(n * (n + 1) / 2);
        cout << endl;
        return;
    }
    while (now >= w)
    {//计算当前一轮减去几个
        ll cnt = now / w;
        //计算cnt-1时的情景
        ll  sum = cnt * w -1;//总数为sum时 ,cnt减少了1
        ll lun = max((now - sum) / cnt,(ll)1);//轮数 , 个数已经确定
        sumlun += lun;
        ll ls1 = lun - 1;
        tcnt.push_back(cnt);
        tlun.push_back(lun);
        id.push_back(id.size());
        now = now - lun * cnt;
    }
    auto cmp = [&](ll a, ll b)->bool
        {
            return tcnt[a] > tcnt[b];
        };
    sort(id.begin(), id.end(), cmp);

    ll tsumlun = sumlun;
    ll tempsum = 0;
    ll tnow = 0;
    for (auto e : id)
    {
        ll cnt = tcnt[e];
        ll lun = tlun[e];
        ll ls1 = lun - 1;
        ll ls2 = ls1 - 1;
        tempsum += tnow*lun+L(1, lun) * L(p, cnt) + (lun * (lun * (lun - 1) / 2) - ls1 * (ls1 + 1) * (2 * ls1 + 1) / 6) * (cnt * cnt);
        tnow += L(p, cnt*lun);
        p += cnt * lun;
        tsumlun -= lun;
    }

    wit((sumlun+1) * L(1, n) - tempsum);
    cout << endl;
    return;
}

int main()
{
    int t = q_;
    while (t--)
    {
        solve();
    }
    return 0;
}


posted @ 2025-08-12 00:45  粉紫系超人气月兔铃仙  阅读(29)  评论(0)    收藏  举报