码迷,mamicode.com
首页 > 其他好文 > 详细

ZR国庆Round2解题报告

时间:2018-10-04 23:02:42      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:自己   while   struct   val   就是   main   pos   心路历程   线段   

心路历程

预计得分:100 + 10 - 20 + 10 = 120

实际得分:100 + 0 + 10 = 110

感觉这场打的挺稳的。开场秒掉A题,写+调差不多1h

然后刚T3暴力,刚完还有2h左右。。然后,,这时候我zz的选择去打T2的暴力,然而T2暴力真的不是一般的难写。。

终于又花了1h打完T2暴力,又打了打$n <= 10$的表,感觉稳的一批。

然后开始跑$n = 11$的,结果到比赛结束也没跑出来qwq。。

离比赛结束还有5min的时候发现T3跟K无关又少拿了20

下午看成绩的时候发现T2 $n = 1$的点玩错了完美爆零。。。。。。。

如果。。。再给我20min让我打完T2的表、、、、

如果。。。再给我15min让我打完T3的优化版暴力。。。。

好像就win了啊可惜没如果。。。。。

T1

单调队列随便做。。。

维护最大最小值,贪心的pop一下就好了。。

因为数据是随机的,所以单调队列里的元素不会很多,deque维护一下。。

#include<bits/stdc++.h>
#define LL long long 
using namespace std;
const int MAXN = 2e7 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
struct Node {
    int val, pos;
}; 
int N, K, seed, l, r; 
deque<Node> qmx, qmn;
int main() {
    N = read(); K = read();
    seed = read(); l = read(), r = read();
    LL ans = 0, pre = 0;
    for(int i = 1; i <= N; i++) {
        int a = seed % (r - l + 1) + l;
        seed = (13331ll * seed + 23333) % 1000000007;
        while(!qmx.empty() && a > qmx.back().val) qmx.pop_back();
        while(!qmn.empty() && a < qmn.back().val) qmn.pop_back();
        //cout << a << endl;
        qmx.push_back((Node) {a, i});
        qmn.push_back((Node) {a, i});   
        while(!qmx.empty() && !qmn.empty() && (1ll * qmx.front().val > 1ll * qmn.front().val * K) ) {
            if(qmx.front().pos < qmn.front().pos) pre = qmx.front().pos, qmx.pop_front();
            else pre = qmn.front().pos, qmn.pop_front();
        }
        if(!qmx.empty() && !qmn.empty()) ans += i - pre;
    }
    cout << ans;
    return 0;
}
/*
20000000 6
1234 4321 8765

*/

T2

这题是真神仙题啊。。。

直接说标算吧。

设$f[i][j]$表示$i$个节点的无根树,最大深度至多为$j$的方案数,

设$g[i][j]$表示$i$个节点的无根树,最大最大深度恰好为$j$的方案数,显然$g[i][j] = f[i][j] - f[i][j - 1]$

那么$f[i][j] = i * \sum_{k = 0}^i \frac{f[i - k][j]}{i - k} * f[k][j - 1] * C_{n - 2}^{k - 1}$

一个公式。。包含了无数个Trick。。Orz xudyh。

首先把无根树计数变成有根树计数,然后枚举与根节点相连的编号最小的点$k$

前面要除掉$i - k$的原因是因为此时我们不清楚根节点

最后$C_{n - 2}^{k - 1}$的意思是我们钦定了根节点和它相连的编号最小的点之后的答案

那么统计答案的时候

  • 如果直径是$2j + 1$,那么答案为

$$\sum g[k][j] * g[N - k][j] * C_{n - 1}^{k - 1}$$

这个应该比较好理解,就是左右分别算一算。

  • 如果直径是$2j$, 答案为

$g[N][j] - \sum g[k][j - 1] * f[N - k][j - 1] * C_n^k$

这个就比较有意思了,解释一下

现在我们钦定了一个根节点,只需要统计以它为中心的答案

$g[n][j]$表示的是深度最大为$j$的方案数,但是这里面会有一些不满足答案(只有一条长为$j$的链)

考虑减去不满足条件的

枚举两棵子树,$g[k][j - 1]$保证了其中的一个满足条件,另一个不满足条件的方案就是从根节点开始深度至多为$j - 1$的方案

标算代码看不懂。。。自己写的调不出来。。咕咕咕。。

#include<bits/stdc++.h>
#define chmax(a, b) (a = (a < b ? b : a))
#define chmin(a, b) (a = (a < b ? a : b))
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
using namespace std;
const int MAXN = 501;
// char buf[1 << 21], *p1 = buf, *p2 = buf;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-')f =- 1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N, M, f[MAXN][MAXN], g[MAXN][MAXN], C[MAXN][MAXN], mod;
int fastpow(int a, int p) {
    int base = 1;
    while(p) {
        if(p & 1) base = 1ll * a * base % mod;
        a = 1ll * a * a % mod; p >>= 1;
    }
    return base;
}
int inv(int x) {
    return fastpow(x, mod - 2);
}
main() {
    N = read(); mod = read();
    C[1][1] = 1;
    for(int i = 2; i <= N; i++) {
        C[i][0] = C[i][i] = 1;
        for(int j = 1; j <= N; j++) C[i][j] = (C[i - 1][j - 1] + C[i][j - 1]) % mod;
    }
    for(int i = 0; i <= N; i++) f[0][i] = g[0][i] = f[1][i] = g[1][i] = 1;
    for(int i = 1; i <= N; i++) {
        f[i][1] = i; 
        for(int j = 2; j <= N; j++) {
            int sum = 0;
            for(int k = 0; k <= i; k++) 
                (sum += 1ll * f[i - k][j] * inv(i - k) % mod * f[k][j - 1] % mod * C[N - 2][k - 1] % mod) %= mod;
            if(!f[i][j]) f[i][j] = 1ll * i * sum % mod;
        }
        for(int j = 0; j <= N; j++) g[i][j] = (f[i][j] - f[i][j - 1] + mod) % mod, printf("%d %d %d\n", i, j, f[i][j]);
    }
    for(int i = 0; i < N; i++) {
        int ans = 0;
        if(i & 1) {
            int j = (i - 1) / 2;
            for(int k = 1; k < N; k++) (ans += 1ll * g[k][j] * g[N - k][j] % mod * C[N - 1][k - 1] % mod) %= mod;
            cout << ans << " ";
        } else {
            int j = i / 2;
            ans = g[N][i];
            for(int k = 1; k < N; k++) ans = (ans - 1ll * g[k][j - 1] * f[N - k][j - 1] % mod * C[N][k] % mod + mod) % mod;
            cout << ans << " ";
        }
    }
}
/*
*/  

T3

首先与$k$无关,因为可以取到$=$号

也就是说每个点只有选或不选两种状态

直接01分数规划,把每个$a[i] - x$,问题转化为能不能删去一些点,使得剩下的权值$>0$

那么一个点能成为答案,当且仅当它不是孤立点且与其他不选的点形成了独立集(因为两条边之间最小有一个要选)

也就是说我们要找出$a[i] - val$最小的独立集,

但是似乎并不好搞,取一下负,找出$val - a[i]$最大的独立集

然后就可以dp了。

$dp[i]$表示考虑右侧的$i$个点,枚举上一个点$j$,再枚举一下左边的点。观察能否加入独立集

第三维前缀和优化一下,时间复杂度:$O(n^2)$

换一种dp方式,加一个$f[j]$表示从$j$转移到当前点的最优代价

for i = 1...n
    dp[i] = max(f[j], 1 <= j < i)
    f[i] = dp[i]
    for all 区间 r = i
    [l, r] = w
    f[0....l - 1]  += W

线段树优化一下,interesting。

复杂度:$O(nlog^2n)$

#include<bits/stdc++.h>
#define chmax(a, b) (a = (a < b ? b : a))
#define chmin(a, b) (a = (a < b ? a : b))
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
#define ls k << 1
#define rs k << 1 | 1 
using namespace std;
const int MAXN = 30001;
const double INF = 1e18, eps = 1e-9;
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-')f =- 1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N, M, K, L[MAXN], R[MAXN];
vector<int> v[MAXN];
double dp[MAXN], a[MAXN], b[MAXN], ta[MAXN], tb[MAXN];
struct Node {
    int l, r, siz;
    double v, f;
}T[MAXN << 2];
void add(int k, double val) {
    T[k].v += val; T[k].f += val;
}
void pushdown(int k) {
    if(T[k].f < eps) return ;
    add(ls, T[k].f); add(rs, T[k].f);
    T[k].f = 0;
}
void update(int k) {
    T[k].v = max(T[ls].v, T[rs].v);
}
void Build(int k, int ll, int rr) {
    T[k].l = ll; T[k].r = rr; T[k].siz = rr - ll + 1; T[k].v = T[k].f = 0;
    if(ll == rr) return ;
    int mid = T[k].l + T[k].r >> 1;
    Build(ls, ll, mid); Build(rs, mid + 1, rr);
    update(k);
}
void IntAdd(int k, int ll, int rr, double val) {
    if(ll <= T[k].l && T[k].r <= rr) {
        add(k, val); return ;
    }
    pushdown(k);
    int mid = T[k].l + T[k].r >> 1;
    if(ll <= mid) IntAdd(ls, ll, rr, val); 
    if(rr >  mid) IntAdd(rs, ll, rr, val);
    update(k);
}
double Query(int k, int ll, int rr) {
    if(ll <= T[k].l && T[k].r <= rr) return T[k].v;
    pushdown(k);
    int mid = T[k].l + T[k].r >> 1;
    if(ll > mid) return Query(rs, ll, rr);
    else if(rr <= mid) return Query(ls, ll, rr);
    else return max(Query(ls, ll, rr), Query(rs, ll, rr));
}
bool check(double val) {
    double sum = 0;
    for(int i = 1; i <= N; i++) ta[i] = max((double)0, val - a[i]), sum += a[i] - val;
    for(int i = 1; i <= M; i++) tb[i] = max((double)0, val - b[i]), sum += b[i] - val;
    Build(1, 0, M);
    for(int i = 1; i <= M + 1; i++) {
        dp[i] = Query(1, 0, i - 1) + tb[i];
        for(int j = 0; j < v[i].size(); j++)
            IntAdd(1, 0, L[v[i][j]] - 1, ta[v[i][j]]);
        if(i != M + 1) IntAdd(1, i, i, dp[i]);
    }
    double ans = 0;
    for(int i = 1; i <= M + 1; i++) ans = max(ans, dp[i]);
    return sum + ans > -eps;
}
main() {
    //freopen("a.in", "r", stdin);
    N = read(); M = read(); K = read();
    double l = INF, r = -INF;
    for(int i = 1; i <= N; i++) a[i] = read(), chmin(l, a[i]), chmax(r, a[i]);
    for(int i = 1; i <= M; i++) b[i] = read(), chmin(l, b[i]), chmax(r, b[i]);
    for(int i = 1; i <= N; i++) {
        L[i] = read(), R[i] = read();
        v[R[i]].push_back(i);
    }
    double ans = -1;
    while(r - l > eps) {
        double mid = (l + r) / 2;
    //  printf("%.10lf\n", mid);
        if(check(mid)) l = mid , ans = mid;
        else r = mid;
    }
    printf("%.10lf", ans);

}

ZR国庆Round2解题报告

标签:自己   while   struct   val   就是   main   pos   心路历程   线段   

原文地址:https://www.cnblogs.com/zwfymqz/p/9743623.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!