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))\)
最后的\((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;
}

浙公网安备 33010602011771号