Loading

NOI Online 2

NOIO 1后凉心CCF又办了一场NOIO 2,估计是再想被攻击一次吧

DAY -1

老师给我们搞了一个模拟赛,具体请看

考的还不错(上次NOIO1前的模拟赛也考得不错,结果只有165pts/kk,具体看

 

想着争取这次上200

DAY 0

因为这次不评奖也不干啥的所以也就没准备什么,颓了一天。

DAY 1

其实也就一天上午

8:00起床,吃个早饭就准备开始比赛了。

这次登陆得很顺利,没怎么卡。

先开T1,范围那么大,感觉是道结论题,不会。

在看T2,感觉以前好像做过类似的题,先不管。

最后看T3,题面写的很迷,手算一遍样例好像不太对。看了下数据范围发现有20pts的链,就随手写了一个。

这时9:00。

回头看T1,往结论上面想,简单推了一下式子,过了样例。(吐槽一下没有大样例,我暴力都不会写

预计得分:100?

这时9:30。

再去看T2,以前好像真的做过类似的题,感觉就是线段树维护一下,一顿乱写过了样例,对拍了一下好像也没问题。

预计得分:100?

此时10:30。

再看T3,突然发现题面好像和之前的不太一样/jk。

按新题面手算了一下样例,发现过了。

然后我就看到了公告:10:10左右改了T3题面(垃圾CCF

发现有20pts的非多项式复杂度暴搜珂写,就随手写了个暴力。(此时11:00)

然后开始想正解,感觉可能是个dp(毕竟不太可能一场比赛没有dp题),然后到最后也没推出来。

预计得分:40?

考试结束后

发现T1好像是道CF原题,CCF这锅得背。

对了一下发现我前两道题都是正解,大概能过。

然后突然发现我T1被hack了,我没判k=1,感觉要凉(毒瘤CCF可以把我卡成0pts)

感觉要凉。

然后又发现T3链的情况1节点不一定是链头,20pts又没了/kk。

最坏得分:0+100+20=120

最好得分:100+100+40=240

出分数

CCF偷偷出分数,我刚开始都不知道。

良心CCF T1还是给了我80pts,T2 100pts没问题,T3就只有20pts了/kk

最终得分:80+100+20=200(终于上了两百

感觉T1没特判还是挺可惜的,要不然就有220pts了。

好多AK的神仙鸭(%%%)

游记就讲到这吧。。。

题解

T1

这道题就不讲每一档部分分怎么拿了,因为我考试的时候也没想。

我们发现如果$k=1$直接输出No就行了,先特判一下。

我们先不妨设$(p_1,p_2)=1$,因为如果$(p_1, p_2) \neq 1$,同时对$p_1, p_2$除以$(p_1, p_2)$效果不变。

再不妨设$p_1<p_2$,最坏情况就是在$[qp_2 + 1, (q+1)p_2 - 1]$里大于等于$k$个$p_1$。

而最坏情况就是$qp_2+1$是$p_1$的倍数,即$rp_1=qp_2+1$。

而如果要输出No,则$(r+k-1)p_1 \leq (q+1)p_2 - 1$,化简的$(k-1)p_1+1<p_2$。

所以若上述式子满足,则输出No,否则输出Yes。

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
ll t, p1, p2, k;
ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}
ll read() {
    ll ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9'){
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        ret = (ret << 1) + (ret << 3) + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
int main() {
//    freopen("color.in", "r", stdin);
//    freopen("color.out", "w", stdout);
    t = read();
    while (t--) {
        p1 = read();
        p2 = read();
        k = read();
        if (k == 1) {
            puts("No");
            continue;
        }
        ll g = gcd(p1, p2);
        p1 /= g;
        p2 /= g;
        if (p1 > p2) swap(p1, p2);
        if (p1 * (k - 1) + 1 < p2) puts("No");
        else puts("Yes");
    }
    return 0;
}

T2

显然可以想到$n^3$的暴力,就是枚举端点,然后计算区间内不同的颜色的数量。

考虑用set维护这个区间,然后增量插入,可以把复杂度降到$n^2\log{n}$,但还是太慢。

枚举右端点r,考虑在log的复杂度内计算出$f(l,r),l \in [1,r]$。

我们记pos=右端点的颜色上一次出现的位置+1,如果没有出现过则记为1,这一点可以通过链表O(n)求出来。

然后我们发现只有[pos,r]这个区间内的l的f(l,r)会较f(l,r-1)加一,区间维护一下平方再区间修改区间查询,这就是线段树裸题了。

注意要离散化否则无法开桶。

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 10;
const ll mod = 1e9 + 7;
struct Segment{
    ll sum, sq, add;
}tr[N << 2];
ll head[N], pre[N];
ll n, a[N], b[N], nn;
ll ans;
ll read() {
    ll ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9'){
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        ret = (ret << 1) + (ret << 3) + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
void push_up(ll p) {
    tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
    tr[p].sq = tr[p << 1].sq + tr[p << 1 | 1].sq;
}
void push_down(ll p, ll l, ll r) {
    ll mid = (l + r) >> 1;
    if (tr[p].add) {
        tr[p << 1].add += tr[p].add;
        tr[p << 1].sq += tr[p].add * tr[p].add * (mid - l + 1) + 2 * tr[p << 1].sum * tr[p].add;
        tr[p << 1].sum += (mid - l + 1) * tr[p].add;
        tr[p << 1 | 1].add += tr[p].add;
        tr[p << 1 | 1].sq += tr[p].add * tr[p].add * (r - mid) + 2 * tr[p << 1 | 1].sum * tr[p].add;
        tr[p << 1 | 1].sum += (r - mid) * tr[p].add;
        tr[p].add = 0;
    }
}
void change(ll p, ll l, ll r, ll x, ll y) {
    if (r < x || l > y) return;
    if (x <= l && r <= y) {
        tr[p].add++;
        tr[p].sq += r - l + 1 + 2 * tr[p].sum;
        tr[p].sum += r - l + 1;
        return;
    }
    push_down(p, l, r);
    ll mid = (l + r) >> 1;
    change(p << 1, l, mid, x, y);
    change(p << 1 | 1, mid + 1, r, x, y);
    push_up(p);
}
int main() {
//    freopen("sequence.in", "r", stdin);
//    freopen("sequence.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; i++) {
        a[i] = read();
        b[++nn] = a[i];
    }
    sort(b + 1, b + 1 + nn);
    nn = unique(b + 1, b + 1 + nn) - b - 1;
    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(b + 1, b + 1 + nn, a[i]) - b;
    }
    for (int i = 1; i <= n; i++) {
        pre[i] = head[a[i]];
        head[a[i]] = i;
    }
    for (int i = 1; i <= n; i++) {
        ll pos = pre[i] + 1;
        change(1, 1, n, pos, i);
        ans = (ans + tr[1].sq) % mod;
    }
    cout << ans;
    return 0;
}

T3

 设 $f_k$ 代表恰好为 $k$ 的方案数,$g_k$ 代表钦定 $k$ 场平局的方案数。显然有 $g_n=\sum_{k\ge n} \binom{k}{n}f_k$,根据二项式反演有 $f_n=\sum_{k\ge n}(-1)^{k-n}\binom{k}{n}g_k$。于是只需算 $g$ 即可。显然可以树形背包。

posted @ 2020-04-30 11:39  Gemini7X  阅读(150)  评论(0编辑  收藏  举报
Title