25南昌IC邀请赛vp补题

博客内容是从易到难(自我认为)

A Nezha Naohai

知识点:无

image

签到,显而易见,把三个数加起来×第四个数就结束了。

M Divide coins

知识点:构造

image

这个题是个构造,自己手模几种情况,很容易发现,如果有k个要反转的,假设n个数左边有x个反转的,右边有k-x个反转的,那么发现如果左边是k个,右边是n-k个数,让左边不反转直接放入左边(1操作),右边反转后放进右边(4操作), 左右正面的数量一定是一样的,故而可得。

void solve()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= m; i++)
            cout << 1;
    for (int i = m + 1; i <= n; i++)
            cout << 4;
    
}

K Rotation

知识点:正难则反,思维

image

正难则反,把一个鼻子固定不动,其他全动,就相当于其他不动,所以枚举每个雕像转到每一种方向的代价,然后最后在分别加上通过第二种操作转到正向的代价,取最小值就行了。

void solve()
{
    cin >> n;
    vi a(n + 1);
    int ans = 1e18;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int j = 0; j < 4; j++)
    {
        int now = 0;
        for (int i = 1; i <= n; i++)
        {
            int d = (a[i] + 4 - j) % 4;
            now += d;
        }
        now += (4 - (now + j) % 4) % 4;
        ans = min(ans, now);
    }
    cout << ans << endl;
}

D Virtuous Pope

知识点:差分,离散化

image

因为平面是有特点的,一定是和三个坐标轴其中之一垂直的,所以我们可以分别计算三个方向的数目,存进数组用离散化即可。

(注意0-based还是1-based)

void solve()
{
    int n, a, b, c;
    cin >> n >> a >> b >> c;
    struct node
    {
        int x, y, z;
        node() {}
        node(int x, int y, int z) : x(x), y(y), z(z) {}
    };
    vector<int> bokx, boky, bokz;
    vector<node> be(n + 7), ed(n + 7);
    for (int i = 1; i <= n; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        be[i] = node(x, y, z);
        bokx.push_back(x), boky.push_back(y), bokz.push_back(z);

        cin >> x >> y >> z;
        ed[i] = node(x, y, z);
        bokx.push_back(x), boky.push_back(y), bokz.push_back(z);
    }

    sort(all(bokx)), sort(all(boky)), sort(all(bokz));
    bokx.erase(unique(all(bokx)), bokx.end());
    boky.erase(unique(all(boky)), boky.end());
    bokz.erase(unique(all(bokz)), bokz.end());

    for (int i = 1; i <= n; i++)
    {
        be[i].x = lower_bound(all(bokx), be[i].x) - bokx.begin() + 1;
        be[i].y = lower_bound(all(boky), be[i].y) - boky.begin() + 1;
        be[i].z = lower_bound(all(bokz), be[i].z) - bokz.begin() + 1;

        ed[i].x = lower_bound(all(bokx), ed[i].x) - bokx.begin() + 1;
        ed[i].y = lower_bound(all(boky), ed[i].y) - boky.begin() + 1;
        ed[i].z = lower_bound(all(bokz), ed[i].z) - bokz.begin() + 1;
    }

    int ans = 0;
    vector<int> cx(n + 7);
    for (int i = 1; i <= n; i++)
    {
        int bex = min(be[i].x, ed[i].x), edx = max(be[i].x, ed[i].x);
        cx[bex]++, cx[edx + 1]--;
    }
    for (int i = 1; i <= n; i++)
    {
        cx[i] += cx[i - 1];
        ans = max(ans, cx[i]);
    }
    vector<int> cy(n + 7);
    for (int i = 1; i <= n; i++)
    {
        int bey = min(be[i].y, ed[i].y), edy = max(be[i].y, ed[i].y);
        cy[bey]++, cy[edy + 1]--;
    }
    for (int i = 1; i <= n; i++)
    {
        cy[i] += cy[i - 1];
        ans = max(ans, cy[i]);
    }
    vector<int> cz(n + 7);
    for (int i = 1; i <= n; i++)
    {
        int bez = min(be[i].z, ed[i].z), edz = max(be[i].z, ed[i].z);
        cz[bez]++, cz[edz + 1]--;
    }
    for (int i = 1; i <= n; i++)
    {
        cz[i] += cz[i - 1];
        ans = max(ans, cz[i]);
    }
    cout << ans << '\n';
}

F Caloric Difference

知识点:贪心

image

image

解析这个公式,p是给出的,也就是表明下一个ci一部分是ci-1,一部分是ri,那么计算ci+1+ci,然后把公式代入发现,ri越小越好。

观察题目,给了部分ri,然后给了ri的范围【l,r】,所以就很好处理了,如果知道ri的值,直接推公式就行,如果不知道,就可以设置为最小值 l ,这样得出来一定是最大的。

void solve()
{
    int n, k;
    cin >> n >> k;
    vector<double> r(n + 7), c(n + 7);
    double p, L, R;
    cin >> r[0] >> c[0] >> p >> L >> R;
    for (int i = 1; i <= k; i++)
    {
        int pos;
        cin >> pos;
        cin >> r[pos];
    }
    for (int i = 1; i <= n; i++)
    {
        if (r[i] == 0)
            r[i] = L;
        c[i] = p * c[i - 1] + (1.0 - p) * r[i - 1];
    }

    double ans = 0;
    for (int i = 1; i <= n; i++)
        ans += c[i] - r[i];
    cout << fixed << setprecision(10) << ans << '\n';
}

G Exploration

知识点:DP

image

image

发现如果对每个点贪心的话,显然不对,这个时候我们就可以考虑考虑dp,

如果考虑每个点的除法的话,时间复杂度不小。又因为向下取整有个小结论,x/a/b=x/a*b

故而我们维护每个点走j步要消耗的代价乘积,然后询问时只需要遍历这个步数就可以了,因为步数很小,最多就30多次,所以时间复杂度完全可以接受。

因为这个dp不是线性的,所以考虑是否有多个维护值,dp[i]代表第i个点,很容易发现发现用dp[i][j]表示第j步以i为起点所消耗的代价乘积。

struct node
{
    int to, w;
};
vector<node> adj[M];
void solve()
{
    int q;
    cin >> n >> m >> q;
    for (int i = 1; i <= m; i++)
    {
        int u, v, d;
        cin >> u >> v >> d;
        adj[u].push_back({v, d});
    }
    vvi dp(n + 1, vi(32, 0));
    for (int i = 1; i <= n; i++)
        dp[i][0] = 1;
    for (int p = 1; p < 32; p++)
    {
        for (int i = 1; i <= n; i++)
        {
            for (auto j : adj[i])
            {
                int num = dp[j.to][p - 1] * j.w;
                num = min(num, INF);
                dp[i][p] = max(dp[i][p], num);
            }
        }
    }
    while (q--)
    {
        int p;
        cin >> p >> x;
        for (int i = 1; i < 32; i++)
        {
            if (dp[p][i] > x)
            {
                cout << i << endl;
                break;
            }
        }
    }
}

I Dating Day

知识点:组合数学,双指针,容斥原理

(这个题说明了翻译的重要性())

image

因为是方案问题,所以肯定有多种操作方法,发现一个连续子段,只要有k个约会,就满足条件了,然后对这个子段进行操作,发现如果这个子段长度越大,这个子段贡献的方案数越多,
而且,你对大子段进行操作,他的方案数一定包括你对小子段的操作吧!所以我们想要一个有且仅有k个约会的超大子段。

但是呢,我们也不难发现,这种超大子段一定会相交吧,所以呢,我们还要用容斥原理去除相交的地方才行。

如果想要最大的方案数,一定是从左端开始找,找到一个就计算贡献C(R-L+1,K),然后接着慢慢往后找,发现中间重合的部分是新的 L2 到旧的 R1,然后很容易发现这里面有k-1个约会,所以重复计算的是这个C(R1-L2+1,K-1),减去这个贡献,接着找。

你也发现了,这就是双指针()加一点容斥原理,再用一点组合数学,好像还真的不难的样子(?

int f[M], ivf[M];
void init()
{
    f[0] = 1;
    for (int i = 1; i < M; i++)
    {
        f[i] = f[i - 1] * i % mod;
    }
    ivf[M - 1] = ksm(f[M - 1], mod - 2, mod);
    for (int i = M - 2; i >= 0; i--)
    {
        ivf[i] = ivf[i + 1] * (i + 1) % mod;
    }
}
int C(int a, int b)
{
    return f[a] * ivf[a - b] % mod * ivf[b] % mod;
}
void solve()
{
    cin >> n >> k >> s;
    s = '#' + s;
    int l = 1, r = 0;
    int now = 0;
    while (r <= n && now <= k)
    {
        r++;
        now += (s[r] == '1');
    }
    now -= (s[r] == '1');
    r--;
    if (now != k)
    {
        cout << 0 << endl;
        return;
    }
    int ans = 0;
    ans += C(r - l + 1, k);
    while (l <= r && r <= n)
    {
        int temp = r;
        while (l <= r && now == k)
        {
            now -= (s[l] == '1');
            l++;
        }
        while (r <= n && now <= k)
        {
            r++;
            now += (s[r] == '1');
        }
        now -= (s[r] == '1');
        r--;
        if (now != k || r < l)
        {
            break;
        }
        ans = (ans + C(r - l + 1, k)) % mod;
        ans = ((ans - C(temp - l + 1, k - 1)) % mod + mod) % mod;
    }
    cout << ans << endl;
}

E God's String on This Wonderful World

知识点:莫队,哈希

虽然是普通莫队,但是是哈希+莫队(),有些细节处理不好就会爆炸()。

image
image

题意是询问你在给定的范围中 有多少子串中26个字母的个数都能整除k。

显然是要预处理一下的,用c数组来计算前缀26个字母的个数,然后我们发现,如果有子串满足那个条件的话,c[r]和c[l]是相等的(mod k),我们未尝不能用这个特点来预处理,计算c数组的种类编号,遇到新的c数组,编号++,两个相等编号的c数组,就是想要的子串吧。

然后用map维护编号,同时用另一个数组记录每一个位置的编号。预处理询问数组,分块,将这个数组的l,r排序,用莫队维护哈希值的数量,用莫队的经典操作来求结果就行了(至于奇偶优化,看个人了),

vi c;
map<vi, int> p;
int ans[M];
int id[M];
int now = 0;
struct node
{
    int li, ri, hi;
} que[M];
int len;
int sum[M];
bool cmp(node a, node b)
{
    if (a.li / len != b.li / len)
    {
        return a.li < b.li;
    }
    return a.ri < b.ri;
}
int res;
void add(int x)
{
    res += sum[x];
    sum[x]++;
}
void del(int x)
{
    sum[x]--;
    res -= sum[x];
}
void solve()
{
    int q;
    cin >> n >> k >> q;
    len = sqrt(n);
    cin >> s;
    s = '#' + s;
    c.resize(26, 0);
    p[c] = ++now;
    id[0] = now;
    for (int i = 1; i <= q; i++)
    {
        cin >> que[i].li >> que[i].ri;
        que[i].li--;
        que[i].hi = i;
    }
    sort(que + 1, que + q + 1, cmp);
    for (int i = 1; i <= n; i++)
    {
        c[s[i] - 'a']++;
        c[s[i] - 'a'] %= k;
        if (!p[c])
        {
            p[c] = ++now;
        }
        id[i] = p[c];
    }

    int l = 1, r = 0;
    for (int i = 1; i <= q; i++)
    {
        while (l > que[i].li)
        {
            add(id[--l]);
        }
        while (r < que[i].ri)
        {
            add(id[++r]);
        }
        while (l < que[i].li)
        {
            del(id[l++]);
        }
        while (r > que[i].ri)
        {
            del(id[r--]);
        }
        ans[que[i].hi] = res;
    }
    for (int i = 1; i <= q; i++)
    {
        cout << ans[i] << endl;
    }
}

先补到这里()

posted @ 2026-04-02 23:09  Lambda_L  阅读(11)  评论(0)    收藏  举报