codeforces 1006 (div 3) 部分题解

1 前言:

从E题开始

2 题解

E https://codeforces.com/contest/2072/problem/E

当且仅当两个点的横坐标或者纵坐标相同时满足曼哈顿距离等于笛卡尔距离。
k<=1e5,同时450450/2 > 1e5,5050/2 > 450 ,根据这两个不等式可以大胆猜测: 当 c(a1,2)+c(a2,2)+..+c(am,2)=k 以不断逼近的形式时成立时 (a1+a2+...+am)<=500。
形如...
...
...
放置就可以保证每个c(x,2)互不干扰,预处理500以内的c(x,2),用二分找到离剩余要满足的对数最近的数即可。

vector<pair<int, int>> ans;
    cin >> k;
    int less = k;
    int totx = 0, toty = 0;
    while (less)
    {
        int index = upper_bound(arr + 1, arr + 1 + 500, less) - arr - 1;
        int maxn = arr[index];
        toty++;
        for (int i = 1; i <= index + 1; i++)
        {
            totx++;
            ans.push_back({totx, toty});
        }
        less -= maxn;
    }
    cout << ans.size() << endl;
    for (int i = 1; i <= ans.size(); i++)
    {
        cout << ans[i - 1].first << ' ' << ans[i - 1].second << endl;
    }

f https://codeforces.com/contest/2072/problem/F

随便手写下样例,容易发现只有k或0,并且2行可由2个1行得来,3-4行可由两个1-2行得来,以此类推满足2的指数增长。例如第10行,可分解为 8 + 2,也就是第十行可由两个2行得出(空余补0),再分解第2行,明显只要写一个返回string的递归就能得到答案。

string dfs(int x)
{
    if (x == 1)
    {
        return "1";
    }
    int tmp = 1;
    while (tmp < x)
    {
        tmp *= 2;
    }
    tmp /= 2;
 
    int nowindex = x - tmp;
    string nowstr = dfs(nowindex);
    string cpstr = nowstr;
    for (int i = nowindex + 1; i <= tmp; i++)
    {
        nowstr += '0';
    }
    return nowstr + cpstr;
}
void solve()
{
    cin >> n >> k;
    string ans = dfs(n);
    for (int i = 0; i < ans.size(); i++)
    {
        if (ans[i] == '0')
        {
            cout << '0' << ' ';
        }
        else
        {
            cout << k << ' ';
        }
    }
    cout << endl;
}

G https://codeforces.com/contest/2072/problem/G

这题是看了别人讲解写出来的。首先,k>n的部分一定只有一位,倒转无意义,直接求和即可。第二,剩下k<=n的部分需要处理,这时应该先看数据允许多大的复杂度,t<=5000,n的总和无限制,所以nt<=1.5e9不能用O(n)的做法,而O(sqrt(n))或O(log(n))都可以,这里我没思路感觉是因为没去考虑O(sqrt(n))的做法,其实可以发现,当sqrt(n) < p <= n时位数都只有两位,这是因为三位的最小数(100)p==pp。那么可以将p<=sqrt(n) 和 p>sqrt(n)的部分分开计算。前一部分小于sqrt(n)可以暴力计算,后一部分,每一个数看作xi,yi。xi最大为sqrt(n),当x位的权值为i,当i+1,x位上的数不变时,y位上的数-=x位上的数。将后一部分按第二位数值的不同划分,也是sqrt(n)个部分,将部分内的求和公式列出,化简,就可以得到每一个划分的答案了。

int n, k, ans = 0;
 
int pown[300005] = {0};
void init()
{
    ans = 0;
}
void solve()
{
    init();
    cin >> n >> k;
    if (k > n)
    {
        ans = ((k - n) % mod) * n % mod;
    }
 
    int index = 2;
    vector<int> arr;
    while (index * index <= n && index <= k)
    {
        int tmp = n;
        arr = vector<int>();
        while (tmp)
        {
            arr.push_back(tmp % index);
            tmp /= index;
        }
        reverse(arr.begin(), arr.end());
 
        int base = 1;
        for (int i = 0; i < arr.size(); i++)
        {
            ans += base * arr[i];
            ans %= mod;
            base *= index;
            base %= mod;
        }
        index++;
    }
 
    while (index <= n && index <= k)
    {
        int a = n % index, d = n / index;
        int m = a / d + 1;
 
        if (index + m - 1 > k)
        {
            m = k + 1 - index;
        }
 
        ans += m * d;
        ans %= mod;
 
        ans += m * a * index;
        ans %= mod;
 
        ans += m * (m - 1) / 2 * (a - index * d);
        ans %= mod;
 
        ans -= pown[m - 1] * d;
        ans = (ans + mod) % mod;
 
        index += m;
    }
 
    cout << (ans + mod) % mod << endl;
}
 
signed main()
{
#ifdef LOCAL_ENV
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(false), cin.tie(0);
    int T = 1;
    cin >> T;
    for (int i = 1; i <= 300000; i++)
    {
        pown[i] = (pown[i - 1] + i * i) % mod;
    }
    for (int i = 1; i <= T; i++)
    {
        solve();
    }
    return 0;
}

3 总结

G题,没能发现以sqrt(n)为界限划分两种做法。
收获:若干项累加找不到规律时,可以抽象为常数和未知数的一个累加式,化简累加式。
p进制(100)==p*p,这个性质可能藏有sqrt(n)的做法。

posted @   青一凡  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示