bzoj violet系列 (2708~2725)

cbh大爷说:写博客不能弃坑。

orz cbh

那我就来更新博客了。

violet这个系列的题好神啊……出题人好劲啊……

……怎么最近都在理性愉悦啊……

另外bzoj400题纪念~

2708: [Violet 1]木偶

首先先对所有木偶按照特征值排序

考虑一种木偶与绳索配对的方法:

木偶\(1\)与绳索\(k+1\)配对,木偶\(2\)与绳索\(k+2\)配对……木偶\(n-k\)与绳索\(n\)配对。

当木偶\(n-k+1\)无法与绳索\(k\)配对时,这样的配对方法能扔掉\(k\)个木偶。

容易证明,最优的配对方式一定可以分成许多段,每段内用这种方式进行配对。

于是可以dp。

首先令\(g_{l,r}\)表示l~r之间用那种方式配对最多能扔掉多少个木偶,\(g_{l,r}\)可以通过枚举\(k\)算出,时间复杂度\(O(n^3)\)

令\(f_i\)表示到1~i之间能扔掉多少个木偶,那么有\(f_i=max\left \{ f_j+g_{j+1,i} \right \}\)。直接dp,时间复杂度\(O(n^2)\)

时间复杂度为\(O(n^2)\)

#include <bits/stdc++.h>
#define N 100
using namespace std;
int n;
int f[N], ai[N];
int g[N][N];
int main()
{
    while (scanf("%d", &n) !=EOF)
    {
        for (int i = 1; i <= n; ++ i) scanf("%d", &ai[i]);
        sort(ai + 1, ai + n + 1);
        for (int i = 1; i <= n; ++ i)
            for (int j = i; j <= n; ++ j)
            {
                int bo = 1;
                for (int k = 1; k <= j - i + 1; ++ k)
                {
                    int p, q;
                    for (p = i, q = i + k; q <= j; ++ p, ++ q)
                        if (ai[q] - ai[p] > 1) bo = 0;
                    if (abs(ai[p] - ai[i + k - 1]) <= 1) bo = 0;
                    if (!bo) {g[i][j] = k - 1; break;}
                }
                if (bo) g[i][j] = j - i + 1;
                //if (g[i][j] != cal(i, j))
                //  puts("haha");
            }
        for (int i = 1; i <= n; ++ i) f[i] = 0;
        for (int i = 1; i <= n; ++ i)
            for (int j = 0; j < i; ++ j)
                f[i] = max(f[i], f[j] + g[j + 1][i]);
        printf("%d\n", f[n]);
    }
}
View Code

 

2709: [Violet 1]迷宫花园

二分答案,判断最短路,期间因为eps的问题wa了许多发QAQ

时间复杂度\(O(RClogRClogv)\)

#include <bits/stdc++.h>
#define INF 1000000000
using namespace std;
int r, c;
double ll;
char mp[110][110];
int get(int rr, int cc)
{
    return (rr - 1) * c + cc;
}
int dr[4] = {1, -1, 0, 0}, dc[4] = {0, 0, 1, -1};
vector <int> bi[10010];
vector <double> ci[10010];
int s, e;
void build(int a, int b, double c)
{
    bi[a].push_back(b); ci[a].push_back(c);
    bi[b].push_back(a); ci[b].push_back(c);
}
struct node
{
    int t; double l;
};
bool operator < (node a, node b) {return a.l > b.l;}
priority_queue <node> H;
int vis[10010]; double dis[10010];
int main()
{
    int T;
    cin >> T;
    while (T --)
    {
        cin >> ll >> r >> c;
        for (int i = 1; i <= r; i++)  
        {  
            char cc;  
            scanf("%c", &cc);  
            while (cc != '\n') scanf("%c", &cc);  
            for (int j = 1; j <= c; j++)  
            {  
                scanf("%c", &cc);  
                while (cc == '\n') scanf("%c",&cc);
                mp[i][j] = cc;
                if (cc=='S') s = get(i, j);
                if (cc=='E') e = get(i, j);
            }  
        }  
        {
            double lb = 0, rb = 10;
            while (rb - lb > 0.0000001)
            {
                double md = (lb + rb) / 2;
                for (int i = 1; i <= r * c; ++ i) bi[i].clear(), ci[i].clear();
                for (int i = 1; i <= r; ++ i)
                    for (int j = 1; j <= c; ++ j)
                        for (int d = 0; d < 4; ++ d)
                            if (mp[i][j] != '#' && mp[i + dr[d]][j + dc[d]] != '#')
                                build(get(i, j), get(i + dr[d], j + dc[d]), (d >= 2? 1: md));
                for (int i = 1; i <= r * c; ++ i)
                    dis[i] = INF, vis[i] = 0;
                while (!H.empty()) H.pop();
                dis[s] = 0,
                H.push((node){s, 0});
                while (!H.empty())
                {
                    int hd;
                    do hd = H.top().t, H.pop();
                    while (vis[hd] && !H.empty()); 
                    if (!vis[hd]) vis[hd] = 1;
                    else break;
                    for (int i = 0; i < bi[hd].size(); ++ i)
                        if (dis[hd] + ci[hd][i] < dis[bi[hd][i]])
                        {
                            dis[bi[hd][i]] = dis[hd] + ci[hd][i];
                            H.push((node){bi[hd][i], dis[bi[hd][i]]});
                        }
                }
                if (dis[e] > ll) rb = md;
                else lb = md;
            }
            printf("%.5lf\n", lb);
        }
                                      
    }
}
View Code

 

2710: [Violet 1]追风者

好神的题啊,我不会做QAQ

 

2711: [Violet 2]After 17

题目要求最小化\(\sum_{i=1}^{n}\sum_{j=i+1}^{n}x_i x_j + y_i y_j\),于是\(x\)和\(y\)可以分别考虑。

\(\sum_{i=1}^{n}\sum_{j=i+1}^{n}x_i x_j = \frac{(\sum_{i=1}^{n}x_i)^2-\sum_{i=1}^{n}x_i^2}{2}\)

通过归纳法(?)可以证明当\(\sum abs(x_i)\)最大时最优。因此\(\sum_{i=1}^{n}x_i)^2\)是定值,于是就可以用dp去求\(\sum_{i=1}^{n}x_i^2\)的最大值。

时间复杂度\(O(nl)\)

#include <bits/stdc++.h>
using namespace std;
#define N 210
#define M 40010
int f[N][M * 2];
int n;
int xi[N], yi[N];
double ans;
void solve(int d[])
{
    for (int i = 0; i <= n; ++ i)
        for (int j = -M; j < M; ++ j)
            f[i][j + M] = 0;
    f[0][M] = 1;
    for (int i = 0; i < n; ++ i)
        for (int j = -M; j < M; ++ j)
            if (f[i][j + M])
                f[i + 1][j + d[i + 1] + M] = f[i + 1][j - d[i + 1] + M] = 1;
    for (int i = 0; i < M; ++ i)
        if (f[n][M + i] || f[n][M - i])
        {
            ans += pow(i, 2);
            break;
        }
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i)
    {
        scanf("%d%d", &xi[i], &yi[i]);
        ans -= pow(xi[i], 2) + pow(yi[i], 2);
    }
    solve(xi);
    solve(yi);
    printf("%.2lf", ans / 2);
}
View Code

 

2712: [Violet 2]棒球

第二道神题啊QAQ

考虑将边界化成分数,为什么要化成分数呢?因为精度问题。

化成分数后就转成了这么个问题:求分数\(\frac{p}{q}\),最小化\(p\),\(q\),使得\(\frac{lp}{lq} < \frac{p}{q} \leq  \frac{rp}{rq}\)

若\(\frac{p}{q} = \frac{rp}{rq}\),那么\(q=\frac{rq}{gcd(rp, rq)}\)。

剩下得就是\(\frac{lp}{lq} < \frac{p}{q} < \frac{rp}{rq}\)

考虑如下做法

  若有一个整数\(p\)满足\(\frac{lp}{lq} < p < \frac{rp}{rq}\),那么就输出\(p\)

  如果\(lp<lq\)且\(rp<rq\),则去递归求\(\frac{rq}{rp} < \frac{p'}{q'} < \frac{lq}{lp}\),那么原问题得解就是\(\frac{q'}{p'}\)

  否则,令\(k=\left \lfloor \frac{lp}{lq} \right \rfloor\),递归求\(\frac{rq - k * rp}{rp} < \frac{p'}{q'} < \frac{lq - k * lp}{lp}\),那么原问题得解就是\(\frac{p' + k * q'}{q'}\)

算法的正确性是显然的。递归时新问题的最优解\(\frac{p'}{q'}\)一定对应原问题的最优解\(\frac{p}{q}\)。如果前者对应的不是后者的最优解,那么就一定可以通过原问题的最优解构造出一个新问题的解\(\frac{p''}{q''}\),使得这个解比\(\frac{p'}{q'}\)更优。

注意边界问题

时间复杂度\(O(r)\)

#include <bits/stdc++.h>
#define LL long long
#define pL pair <LL, LL>
using namespace std;
int n; LL r;
pL solve(LL lu, LL ld, LL ru, LL rd)
{
    LL x = lu / ld + 1;
    if (x * rd < ru) return pL(x, 1);
    if (!lu) return pL(1, rd / ru + 1);
    if (lu <= ld && ru <= rd) 
        {pL nw = solve(rd, ru, ld, lu); return pL(nw.second, nw.first);}
    x = lu / ld;
    pL nw = solve(lu - ld * x, ld, ru - rd * x, rd);
    nw.first += nw.second * x;
    return nw;
}
int main()
{
    while (scanf("%d 0.%lld", &n, &r) == 2)
    {
        if (r == 0) {puts("1"); continue;}
        LL x = 10; while (n --) x *= 10;
        LL lu = r * 10 - 5, ld = x, ru = r * 10 + 5, rd = x;
        for( ; lu % 5 == 0 && ld % 5 == 0; lu /= 5, ld /= 5);
        for( ; ru % 5 == 0 && rd % 5 == 0; ru /= 5, rd /= 5);
        pL ans = solve(lu, ld, ru, rd);
        //printf("%lld %lld\n", ans.first, ans.second);
        printf("%lld\n", min(ans.second, ld));
    }
}
View Code

 

2713: [Violet 2]愚蠢的副官

第三道神题……我dp被卡常数了,极限数据要跑8s……交标程走人

 

 

2714: [Violet 3]交替和

数位dp

定义一个数的交替和为将该数从高位到低位添加至序列L后,A(L)的值,比如\(1101B\)的交替和为\(-1\)

定义\(f_{0,i}\)表示b进制下,所有可以有前导零的i位数的交替和的和。

定义\(f_{1,i}\)表示b进制下,所有可以有前导零的i位偶数的交替和减去所有可以有前导零的i位奇数的交替和

设\(n\)在\(b\)进制下有\(len\)位

那么答案由两部分组成。第一部分是位数在\(1\)~\(len-1\)之间的数,第二部分是位数为\(len\)的数。

  维护当前要计算的第一个数位对答案的贡献的系数\(np\)。

  第一部分枚举位数,最高位,用\(f\)以及\(np\)计算贡献
  第二部分枚举与n从第几位开始不同,以及这一位选了多少,依然用\(f\)以及\(np\)计算贡献

时间复杂度\(O(blogn)\)

/**************************************************************
    Problem: 2714
    User: AwD
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:1296 kb
****************************************************************/
 
#include <bits/stdc++.h>
#define LL long long
using namespace std;
LL b, n;
LL f[2][100], g[100];
LL ni[100], nl, ans;
int main()
{
    cin >> b >> n;
    g[0] = 1;
    for (LL i = 1, j = 1; j <= n; ++ i, j *= b)
    {
        LL np = 1;
        for (LL k = 0; k < b; ++ k)
            f[0][i] += f[0][i - 1] * -1 + g[i - 1] * k,
            f[1][i] += f[1][i - 1] * -np + (g[i - 1] & 1) * k * np,
            np = np * ((g[i - 1] * i)& 1? -1: 1);
        g[i] = g[i - 1] * b;
    }
    while (n)
    {
        ni[++ nl] = n % b;
        n /= b;
    }
    {
        LL np = 1, s = 0;
        for (LL i = 1; i < nl; ++ i)
        {
            for (LL k = 1; k < b; ++ k)
                ans += f[i & 1][i - 1] * -np + (i & 1? g[i - 1] & 1: g[i - 1]) * k * np,
                np = np * ((g[i - 1] * i) & 1? -1: 1);
        }
        //cout << ans << "\n";
 
        for (LL i = nl; i >= 1; -- i)
        {
            for (LL k = (i == nl); k <= (ni[i] - (i != 1)); ++ k)
                ans += f[nl & 1][i - 1] * ((nl - i) & 1? 1: -1) * np + (nl & 1? g[i - 1] & 1: g[i - 1]) * (s + ((nl - i) & 1? -1: 1) * k) * np,
                np = np * ((g[i - 1] * nl) & 1? -1: 1);
            s += ((nl - i) & 1? -1: 1) * ni[i];
        }
        cout << ans << "\n";
    }
}
View Code

 

2715: [Violet 3]最优指令

状压所有状态是否可以出现,简单bfs即可

时间复杂度\(O(2^SC)\)

#include <bits/stdc++.h>
#define N (1 << 17)
using namespace std;
int s, c;
int dis[N], vis[N];
queue <int> Q;
int pres[N], prei[N];
int trans[18][18];
void dfs(int t)
{
    if (t != (1 << s) - 1)
    {
        dfs(pres[t]);
        if (prei[t] < 10) printf("%d", prei[t]);
        else printf("%c", prei[t] - 10 + 'a');
    }
}
int main()
{
    scanf("%d%d", &s, &c);
    for (int i = 0; i < s; ++ i)
        for (int j = 0; j < c; ++ j)
            scanf("%d", &trans[i][j]);
    Q.push((1 << s) - 1); vis[(1 << s) - 1] = 1;
    while (!Q.empty())
    {
        int hd = Q.front(); Q.pop();
        for (int i = 0; i < c; ++ i)
        {
            int nw = 0;
            for (int j = 0; j < s; ++ j)
                if (hd & (1 << j))
                    nw |= (1 << trans[j][i]);
            if (!vis[nw])
            {
                vis[nw] = 1;
                dis[nw] = dis[hd] + 1;
                pres[nw] = hd;
                prei[nw] = i;
                Q.push(nw);
            }
        }
    }
    if (!vis[1]) puts("impossible");
    else dfs(1);
}
View Code

 

2716: [Violet 3]天使玩偶

裸的k-d树,我的常数写的好挫啊QAQ

时间复杂度\(O((n+m)\sqrt{n + m})\)

#include <bits/stdc++.h>
#define INF 100000000
#define N 1000100
using namespace std;
int n, m;
struct point {int d[2];};
struct node 
{
    int opt, ch[2], si;
    int bd[2][2];
    point mid;
} tr[N];
stack <int> S;
int nw, tmp;
int comp(point a, point b) {return a.d[nw] < b.d[nw];}
point ls[N];
void dfs_dele(int t)
{
    if (tr[t].ch[0]) dfs_dele(tr[t].ch[0]);
    ls[++ tmp] = tr[t].mid;
    if (tr[t].ch[1]) dfs_dele(tr[t].ch[1]);
    tr[t].ch[0] = tr[t].ch[1] = 0;
    tr[t].opt = tr[t].si = 0;
    S.push(t);
}
void update(int t)
{
    tr[t].bd[0][0] = min(tr[t].mid.d[0], min(tr[tr[t].ch[0]].bd[0][0], tr[tr[t].ch[1]].bd[0][0]));
    tr[t].bd[0][1] = max(tr[t].mid.d[0], max(tr[tr[t].ch[0]].bd[0][1], tr[tr[t].ch[1]].bd[0][1]));
    tr[t].bd[1][0] = min(tr[t].mid.d[1], min(tr[tr[t].ch[0]].bd[1][0], tr[tr[t].ch[1]].bd[1][0]));
    tr[t].bd[1][1] = max(tr[t].mid.d[1], max(tr[tr[t].ch[0]].bd[1][1], tr[tr[t].ch[1]].bd[1][1]));
}
int rebuild(int opt, int l, int r)
{
    nw = opt; sort(ls + l, ls + r + 1, comp);
    int mid = (l + r) / 2;
    int hd = S.top(); S.pop();
    tr[hd].mid = ls[mid];
    tr[hd].opt = opt; tr[hd].si = r - l + 1;
    if (l < mid) tr[hd].ch[0] = rebuild(!opt, l, mid - 1);
    if (mid < r) tr[hd].ch[1] = rebuild(!opt, mid + 1, r);
    update(hd);
    return hd;
}
int insert(int opt, int t, point nw)
{
    if (!t)
    {
        t = S.top(); S.pop();
        tr[t].opt = opt;
        tr[t].si = 1; tr[t].mid = nw;
    }
    else
    {
        int k = tr[t].mid.d[tr[t].opt] < nw.d[tr[t].opt];
        tr[t].si ++;
        tr[t].ch[k] = insert(!opt, tr[t].ch[k], nw);
        if (1.0 * tr[tr[t].ch[k]].si / tr[t].si > 0.7)
        {
            tmp = 0; dfs_dele(t);
            t = rebuild(opt, 1, tmp);
        }
    }
    update(t);
    return t;
}
int nowans;
int dis(point a, point b)
{
    return abs(a.d[0] - b.d[0]) + abs(a.d[1] - b.d[1]);
}
int dis(point a, int t)
{
    int tmp = 0;
    for (int i = 0; i <= 1; ++ i)
        if (a.d[i] < tr[t].bd[i][0]) tmp += tr[t].bd[i][0] - a.d[i];
    for (int i = 0; i <= 1; ++ i)
        if (a.d[i] > tr[t].bd[i][1]) tmp += a.d[i] - tr[t].bd[i][1];
    return tmp;
}
int s, dp;
void ask(int t, point nw)
{
    s ++; 
    if (dis(tr[t].mid, nw) < nowans) nowans = dis(tr[t].mid, nw);
    int b[2];
    b[0] = dis(nw, tr[t].ch[0]);
    b[1] = dis(nw, tr[t].ch[1]);
    int k = b[1] < b[0];
    if (tr[t].ch[k]) if (b[k] < nowans) ask(tr[t].ch[k], nw);
    if (tr[t].ch[!k]) if (b[!k] < nowans) ask(tr[t].ch[!k], nw);
    //dp --;
}
int root;
int main()
{
    tr[0].bd[0][0] = tr[0].bd[1][0] = INF;
    scanf("%d%d", &n, &m);
    for (int i = N - 1; i >= 1; -- i) S.push(i);
    for (int i = 1; i <= n + m; ++ i)
    {
        int t = 1, x, y;
        if (i > n) scanf("%d", &t);
        scanf("%d%d", &x, &y);
        nowans = INF;
        if (t == 1) root = insert(0, root, (point){x, y});
        else
        {
            s = 0;
            ask(root, (point){x, y});
            printf("%d\n", nowans);
        }
    }
}
View Code

 

2717: [Violet 4]迷路的兔子

第四道神题= =

奥妙重重的构造题,只要知道这是对的就好了是不是哇QAQ

时间复杂度\(O(n^2)\)

#include <bits/stdc++.h>
#define N 1000
using namespace std;
int n, x;
int remain[N][N];
int main()
{
    cin >> n;
    cout << n * (n - 1) / 2 << "\n";
    for (int i = 1; i <= (n >> 1); ++ i)
        for (int j = 1; j <= n; ++ j)
            cout << j << " " << (j + i - 1) % n + 1 << " " << (j + i + i - 1) % n + 1 << "\n";
}
View Code

 

2718: [Violet 4]毕业旅行

建二分图,把每个点\(i\)拆成两个点\(A_i\),\(B_i\)

用传递闭包把原图中每个点能到达的点都跑出来,若\(i\)能到达\(j\),那么就在新图中将\(A_i\)与\(B_j\)连一条边。

那么原图的独立集就相当于新图中的独立集。直接上匈牙利。

时间复杂度\(O(n^3)\)

#include <bits/stdc++.h>
using namespace std;
int n, m;
int sx[210][210];
int vx[210], vy[210], pr[210];
int ans;
int dfs(int t)
{
    vx[t] = 1;
    for (int i = 1; i <= n; ++ i)
        if (sx[t][i] && !vy[i] && !vx[pr[i]])
        {
            if (!pr[i] || dfs(pr[i]))
            {
                pr[i] = t;
                return 1;
            }
        }
    return 0;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; ++ i)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        sx[a][b] = 1;
    }
    for (int k = 1; k <= n; ++ k)
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= n; ++ j)
                if (sx[i][k] && sx[k][j])
                    sx[i][j] |= 1;
    for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j <= n; ++ j) vx[j] = vy[j] = 0;
        if (dfs(i)) ans ++;
    }
    printf("%d", n - ans);
}
View Code

 

2719: [Violet 4]银河之星

由于第二种移动的存在,所有在模三意义下同余的位置是等价的,因此可以缩在一起。

直接上记搜。

注意两个坑点:

  1.由于棋盘大小的限制,某些情况下第二种移动是没法做的,因此要现判断这些移动的可行性。

  2.由于棋盘大小的限制,每种位置能放的棋子的数量是有上限的,搜索时要判断一下

时间复杂度O(跑的出)

#include <bits/stdc++.h>
using namespace std;
int k, n, m, fin_x, fin_y;
int fin;
struct status
{
    int ss[9];
    status () {for (int i = 0; i < 9; ++ i) ss[i] = 0;}
};
int operator <(status a, status b)
{
    for (int i = 0; i < 9; ++ i)
        if (a.ss[i] != b.ss[i])
        return a.ss[i] < b.ss[i];
    return 0;
}
map <status, int> M;
int xd[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
int yd[8] = {-1, 0, 1, 1, 1, 0, -1, -1};
int ai[1000], bi[1000], ci[1000], tot;
int cg[9][9], mx[9];
int get_pos(int x, int y)
{
    x = x % 3;
    y = y % 3;
    return x * 3 + y;
}
int dfs(status nw)
{
    if (M.count(nw)) return M[nw];
    int is_ans = 1;
    for (int i = 0; i < 9; ++ i)
        if (((bool)nw.ss[i]) ^ (i == fin))
            is_ans = 0;
    if (is_ans) return 1;
    for (int i = 0; i < tot; ++ i)
        if (nw.ss[ai[i]] && nw.ss[bi[i]])
        {
            status nx = nw;
            nx.ss[ai[i]] --;
            nx.ss[bi[i]] --;
            nx.ss[ci[i]] ++;
            if (nx.ss[ci[i]] > mx[ci[i]]) continue;
            if (dfs(nx)) return M[nw] = 1;
        }
    return M[nw] = 0;
}
void init()
{
    for (int i = 0; i < 9; ++ i)
        for (int j = 0; j < 9; ++ j)
            cg[i][j] = -1;
    for (int i = 0; i < 9; ++ i) mx[i] = 0;
    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= m; ++ j)
        {
            mx[get_pos(i, j)] ++;
            for (int d = 0; d < 8; ++ d)
                if (i + xd[d] * 2 >= 1 && i + xd[d] * 2 <= n && j + yd[d] * 2 >= 1 && j + yd[d] * 2 <= m)
                    cg[get_pos(i, j)][get_pos(i + xd[d], j + yd[d])] = get_pos(i + xd[d] * 2, j + yd[d] * 2);
        }
    tot = 0;
    for (int i = 0; i < 9; ++ i)
        for (int j = 0; j < 9; ++ j)
            if (cg[i][j] >= 0)
            {
                ai[tot] = i;
                bi[tot] = j;
                ci[tot] = cg[i][j];
                tot ++;
            }
}
int main()
{
    while (scanf("%d%d%d%d%d", &k, &n, &m, &fin_x, &fin_y) == 5)
    {
        fin = get_pos(fin_x, fin_y);
        M.clear();
        status start;
        for (int i = 1; i <= k; ++ i)
        {
            int x, y; 
            scanf("%d%d", &x, &y);
            start.ss[get_pos(x, y)] ++;
        }
        init();
        if (dfs(start)) puts("Yes");
        else puts("No");
    }
}
View Code

 

2720: [Violet 5]列队春游

数学题,推推式子可以得到结论

时间复杂度\(O(nlogn)\)

#include <bits/stdc++.h>
using namespace std;
int n, s;
int ai[400];
double ans;
 
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) scanf("%d", &ai[i]);
    sort(ai + 1, ai + n + 1);
    for (int i = 1, j = 0; i <= n; ++ i)
        if (ai[i] != ai[i + 1])
        {
            ans += 1.0 * (i - j) * (n + 1) / (n + 1 - j);
            j = i;
        }
    printf("%.2lf", ans);
}
View Code

 

2721: [Violet 5]樱花

又是一道数学题,答案是\((n!)^2\)的约数个数,筛一下素数即可

时间复杂度\(O(n)\) (还是\(O(nloglogn)\)?)

#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define ll long long
#define mod 1000000007
#define inf 1000000000
using namespace std;
int read()
{
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,cnt;
ll ans=1;
int pri[1000005],mn[1000005],num[1000005];
bool del[1000005];
void getpri()
{
    for(int i=2;i<=n;i++)
    {
        if(!del[i])pri[++cnt]=i,mn[i]=cnt;
        for(int j=1;pri[j]*i<=n&&j<=cnt;j++)
        {
            del[pri[j]*i]=1;mn[pri[j]*i]=j;
            if(i%pri[j]==0)break;
        }
    }
}
void cal(int x)
{
    while(x!=1)
    {
        num[mn[x]]++;
        x/=pri[mn[x]];
    }
}
int main()
{
    n=read();
    getpri();
    for(int i=1;i<=n;i++)
        cal(i);
    for(int i=1;i<=cnt;i++)
        ans=ans*(num[i]*2+1)%mod;
    printf("%lld\n",ans);
    return 0;
}
View Code

 

2722: [Violet 5]爱的花环

还是神题= =

由于\(A_{i,j}=A_{j,i}\)

因此\(l_{i,j}\)和\(l_{j,i}\)的限制,\(r_{i,j}\)和\(r_{j,i}\)的限制可以分别合并在一起。

且\(\sum_{i=1}^{n}\sum_{j=1}^{n}A_{i,j}=0\)可以化为:\(\sum_{i=1}^{n}A_{i,i} + 2\sum_{i=1}^{n}\sum_{j=i+1}^{n}A_{i,j}=0\)。

考虑做以下变换:

  对于所有满足\(i!=j\)的数对\((i,j)\),将\(l_{i,j}\)、\(r_{i,j}\)翻倍。

  对于所有\(k_{i,i}\),将其翻倍。

在这个变换之后,原问题就转化为了:求一个满足

  \(\sum_{i=1}^{n}\sum_{j=i}^{n}A_{i,j}=0\),

  \(l_{i,j}\leq A_{i,j}\leq r_{i,j}\),

  对于所有\(i != j\),有\(A_{i,j}mod2=0\)

这些限制的数组\(A\),使得\(\sum_{i=1}^{n}\sum_{j=i}^{n}k_{i,j}A_{i,j}\)最大。

这个问题得到的答案是原问题的两倍。

现考虑如何解决这个问题。

首先先排除对于所有\(i != j\),有\(A_{i,j}mod2=0\)这个限制,由于\(k_{i,j}\)为正,可以这样做:

  1.将所有\(A_{i,j}\)赋值为\(r_{i,j}\)

  2.如果当前\(\sum_{i=1}^{n}\sum_{j=i}^{n}A_{i,j}=0\),结束,返回答案

  3.否则在满足\(A_{i,j}!=l_{i,j}\)的数对\((i,j)\)中找一个\(k_{i,j}\)最小的,将它的\(A_{i,j}\)减小,减小到满足下面任意一条限制为止:

    a.\(\sum_{i=1}^{n}\sum_{j=i}^{n}A_{i,j}=0\)

    b.\(A_{i,j}=l_{i,j}\)

  4.转至步骤2

这个贪心的正确性是明显的。

那么加上对于所有\(i != j\),有\(A_{i,j}mod2=0\) 这个限制。考虑会发生什么:

  可能会出现一个\(A_{i,j}(i != j)\)变成奇数了。

为了满足这个限制,需要改变它的奇偶性。这个值从哪里来?只能从某一个\(A_{p,p}\)中来。

让答案尽可能的优,我们需要找到一个\(abs(k_{p,p} - k_{i,j})\)最小的\((p, p)\),并在\((p, p)\),\((i, j)\)之间移动一个1即可。

然后就是这样了……

时间复杂度\(O(n^2logn^2)\)

#include <bits/stdc++.h>
#define N 300000
#define INF 100000000
#define LL long long
using namespace std;
LL n;
LL ss[600][600], pp[600][600], tot;
LL ki[N], li[N], ri[N];
LL nw[N];
LL ord[N], nwsum, ans;
LL comp(LL a, LL b)
{
    return ki[a] < ki[b];
}
int main()
{
    //freopen("garland6.in", "r", stdin);
    scanf("%lld", &n);
    for (LL i = 1; i <= n; ++ i) ss[i][i] = ++ tot;
    for (LL i = 1; i <= n; ++ i)
        for (LL j = i + 1; j <= n; ++ j)
            ss[i][j] = ss[j][i] = ++ tot;
    for (LL i = 1; i <= n * (n - 1); ++ i)
        li[i] = -INF, ri[i] = INF, ki[i] = 0;
    for (LL i = 1; i <= n; ++ i)
        for (LL j = 1; j <= n; ++ j)
        {
            LL a;
            scanf("%lld", &a);
            ki[ss[i][j]] += a;
        }
    for (LL i = 1; i <= n; ++ i)
        for (LL j = 1; j <= n; ++ j)
        {
            LL a;
            scanf("%lld", &a);
            li[ss[i][j]] = max(li[ss[i][j]], a);
        }
    for (LL i = 1; i <= n; ++ i)
        for (LL j = 1; j <= n; ++ j)
        {
            LL a;
            scanf("%lld", &a);
            ri[ss[i][j]] = min(ri[ss[i][j]], a);
        }
    for (LL i = 1; i <= n; ++ i) ki[i] *= 2;
    for (LL i = n + 1; i <= tot; ++ i) li[i] *= 2, ri[i] *= 2;
    //for (LL i = 1; i <= tot; ++ i) cout << li[i] << " "; cout << "\n";
    //for (LL i = 1; i <= tot; ++ i) cout << ri[i] << " "; cout << "\n";
    //for (LL i = 1; i <= tot; ++ i) cout << ki[i] << " "; cout << "\n";
    for (LL i = 1; i <= tot; ++ i) ord[i] = i;
    sort(ord + 1, ord + tot + 1, comp);
    for (LL i = 1; i <= tot; ++ i) nw[i] = ri[i], nwsum += ri[i];
    for (LL i = 1; i <= tot; ++ i)
        if (nwsum > ri[ord[i]] - li[ord[i]]) nwsum -= ri[ord[i]] - li[ord[i]], nw[ord[i]] = li[ord[i]];
        else if (ord[i] <= n || nwsum % 2 == 0)
        {
            nw[ord[i]] -= nwsum; break;
        }
        else
        {
            nw[ord[i]] -= nwsum;
            LL p = i, q = i;
            while (ord[q] > n && q <= tot) ++ q;
            while (ord[p] > n && p >= 1) -- p;
            if (p < 1 || (q <= tot && ki[ord[q]] - ki[ord[i]] < ki[ord[i]] - ki[ord[p]]))
            {
                nw[ord[i]] ++;
                nw[ord[q]] --;
            }
            else
            {
                nw[ord[i]] --;
                nw[ord[p]] ++;
            }
            break;
        }
    for (LL i = 1; i <= n; ++ i)
        for (LL j = 1; j <= n; ++ j)
            if (i == j)
                pp[i][j] = nw[ss[i][j]];
            else
            {
                pp[i][j] = nw[ss[i][j]] / 2;
                if (nw[ss[i][j]] % 2 == 1) puts("mdzz");
            }
            /*
    for (LL i = 1; i <= n; ++ i)
    {
        for (LL j = 1; j <= n; ++ j) printf("%d ", pp[i][j]);
        puts("");
    }*/
    for (LL i = 1; i <= tot; ++ i) ans += nw[i] * ki[i];
    printf("%lld", ans / 2);
}
View Code

 

2723: [Violet 6]星之波动

好像很神的样子QAQ,不敢看

 

2724: [Violet 6]蒲公英

很显然的分块,l~r之间的众数只可能出现在:l~块a的众数,块a~块b的众数,块b~r的众数之间,预处理出任意两块之间的众数,乱搞即可。

时间复杂度\(O(n\sqrt{n})\)

#include <bits/stdc++.h>
#define N 50000
#define P 1000
using namespace std;
int n, m, s;
int ai[N], si[N];
int mx[P][P], in[N], st[N];
vector <int> L[N], S;
int nowi[N], nowmax;
int get(int t, int l, int r)
{
    int p = (upper_bound(L[t].begin(), L[t].end(), r) - lower_bound(L[t].begin(), L[t].end(), l)) * (n + 1) - t;
    return p;
}
int main()
{
    scanf("%d%d", &n, &m); s = sqrt(n / (log(n) / log(2))) + 1;
    for (int i = 1; i <= n; ++ i)
        scanf("%d", &ai[i]), si[i] = ai[i];
    sort(si + 1, si + n + 1);
    for (int i = 1; i <= n; ++ i)
        ai[i] = lower_bound(si + 1, si + n + 1, ai[i]) - si;
    for (int i = 1; i <= n; ++ i) L[ai[i]].push_back(i);
    for (int i = 1, p = 1; i <= n; i += s, ++ p)
        for (int j = 1; j <= s && i + j - 1 <= n; ++ j)
            in[i + j - 1] = p;
     
    for (int i = 1; i <= n; i += s)
    {
        for (int j = 1; j <= n; ++ j) nowi[j] = 0;
        for (int j = i; j <= n; ++ j)
        {
            nowi[ai[j]] ++;
            if (nowi[ai[j]] > nowi[nowmax] || nowi[ai[j]] == nowi[nowmax] && ai[j] < nowmax) 
                nowmax = ai[j];
            if (in[j] != in[j + 1]) mx[in[i]][in[j]] = nowmax;
        }
    }
    for (int i = 1, last = 0; i <= m; ++ i)
    {
        int l, r, ans = 0;
        scanf("%d%d", &l, &r);
        //last = 0;
        l = (l + last - 1) % n + 1;
        r = (r + last - 1) % n + 1;
        if (l > r) swap(l, r);
        S.clear();
        if (in[l] == in[r]) for (int i = l; i <= r; ++ i) S.push_back(ai[i]);
        else
        {
            for (int i = l; in[i] == in[l]; ++ i) S.push_back(ai[i]);
            for (int i = r; in[i] == in[r]; -- i) S.push_back(ai[i]);
            S.push_back(mx[in[l] + 1][in[r] - 1]);
        }
        for (int i = 0; i < S.size(); ++ i)
            if (get(S[i], l, r) > get(ans, l, r))
                ans = S[i];
        ans = si[ans];
        printf("%d\n", ans);
        last = ans;
    }
}
View Code

 

2725: [Violet 6]故乡的梦

终于要写完了QAQ

首先从\(s\)开始建最短路径生成树。

定义关键路径为该树上\(s\)->\(t\)的路径。

定义一个点的\(dep\)

  若一个点在关键路径上,则该点的\(dep\)为它是关键路径上的第几个点。

  若一个点不在关键路径上,则该点的\(dep\)为它沿着树边走,到达第一个在关键路径上的点的\(dep\)。

很明显,如果切断的边\((x, y)\)如果不在关键路径上的话,最短路是不会变的。

因此考虑\((x, y)\)在树上的情况,假定x是y的父亲。

考虑这时的路径是怎样的:\(s\)->\(x'\)->\(y'\)->\(t\)

其中\(s\)->\(x'\)是在原图上\(s\)到\(x'\)的最短路,且\(x'\)尽可能的晚,\(y'\)->\(t\)是原图上\(y'\)到\(t\)的最短路,且\(y'\)尽可能的早。

这三段一定不能含有\(x\)->\(y\)

首先考虑\(s\)->\(x'\)这一段,要使得其中没有\(x\)->\(y\),只需要保证\(dep_{x'}\leq dep_{x}\)即可

接着考虑\(y'\)->\(t\)这一段,要使得其中没有\(x\)->\(y\),只需要保证\(dep_{y}\leq dep_{y'}\)即可。由于最短路不会绕一圈,这个的正确性是显然的。

最后考虑\(x'\)->\(y'\)这一段

  假设\(x'\)->\(y'\)不止一条边,那么设其中有一个点\(a\),因此这一段就变成了\(x'\)->\(a\)->\(y'\)

  但是由于只切断了一条边,因此\(a\)到\(s\)或者\(t\)中一个点的路径一定是最短路,于之前的设定矛盾。

  因此\(x'\)->\(y'\)只有一条边。

于是我们可以枚举每条不在最短路图上的边\((a, b)\),用它去更新切断边在\(\left [  dep_{a},dep_{b} \right ]\)内的答案。用线段树维护即可

时间复杂度\(O((n+m)logn)\)

 

#include <bits/stdc++.h>
#define N 300000
#define M 900000
#define INF 1000000000000000ll
#define LL long long
using namespace std;
LL n, m, s, e;
vector <LL> bi[N], ci[N];
LL vis[N];
LL dis[N], die[N], pre[N], lst[N], tot;
LL inls[N];
struct node {LL t, d;};
struct comp {LL operator () (node a, node b) {return a.d > b.d;}};
priority_queue <node, vector <node>, comp> H;
 
queue <LL> Q;
 
void dfs1(int t, int p)
{
    inls[t] = p;
    for (LL i = 0; i < bi[t].size(); ++ i)
        if (pre[bi[t][i]] == t)
            if (inls[bi[t][i]]) dfs1(bi[t][i], inls[bi[t][i]]);
            else dfs1(bi[t][i], p);
}
 
LL mn[M], finmn[N];
void put_min(LL t, LL lb, LL rb, LL l, LL r, LL d)
{
    if (lb == l && rb == r) {mn[t] = min(mn[t], d); return;}
    LL ml = (lb + rb) / 2, mr = (lb + rb) / 2 + 1;
    if (l <= ml) put_min(t * 2    , lb, ml, l, min(r, ml), d);
    if (r >= mr) put_min(t * 2 + 1, mr, rb, max(l, mr), r, d);
}
void dfs_seg(LL t, LL lb, LL rb)
{
    if (lb != rb)
    {
        mn[t * 2] = min(mn[t * 2], mn[t]);
        dfs_seg(t * 2, lb, (lb + rb) / 2);
        mn[t * 2 + 1] = min(mn[t * 2 + 1], mn[t]);
        dfs_seg(t * 2 + 1, (lb + rb) / 2 + 1, rb);
    }
    else finmn[lb] = mn[t];
}
LL q;
int main()
{
    scanf("%lld%lld", &n, &m);
    for (LL i = 1; i <= m; ++ i)
    {
        LL a, b, c;
        scanf("%lld%lld%lld", &a, &b, &c);
        bi[a].push_back(b); ci[a].push_back(c);
        bi[b].push_back(a); ci[b].push_back(c);
    }
    scanf("%lld%lld", &s, &e);
    for (LL i = 1; i <= n; ++ i) 
        dis[i] = INF;
    dis[s] = 0; H.push((node){s, 0});
    while (!H.empty())
    {
        LL hd;
        do hd = H.top().t, H.pop();
        while (vis[hd] && !H.empty());
        if (vis[hd]) break; else vis[hd] = 1;
        for (LL i = 0; i < bi[hd].size(); ++ i)
            if (dis[hd] + ci[hd][i] < dis[bi[hd][i]])
                dis[bi[hd][i]] = dis[hd] + ci[hd][i],
                pre[bi[hd][i]] = hd,
                H.push((node){bi[hd][i], dis[bi[hd][i]]});
    }
     
    for (LL i = 1; i <= n; ++ i) vis[i] = 0;
    for (LL i = 1; i <= n; ++ i) 
        die[i] = INF;
    die[e] = 0; H.push((node){e, 0});
    while (!H.empty())
    {
        LL hd;
        do hd = H.top().t, H.pop();
        while (vis[hd] && !H.empty());
        if (vis[hd]) break; else vis[hd] = 1;
        for (LL i = 0; i < bi[hd].size(); ++ i)
            if (die[hd] + ci[hd][i] < die[bi[hd][i]])
                die[bi[hd][i]] = die[hd] + ci[hd][i],
                H.push((node){bi[hd][i], die[bi[hd][i]]});
    }
     
    for (LL p = e; p; p = pre[p])
        lst[++ tot] = p, inls[p] = tot;
     
    dfs1(s, inls[s]);
    for (LL i = 1; i <= tot * 4; ++ i) mn[i] = INF;
    for (LL i = 1; i <= n; ++ i)
        for (LL j = 0; j < bi[i].size(); ++ j)
            if (inls[i] > inls[bi[i][j]] && pre[bi[i][j]] != i)
                put_min(1, 1, tot, inls[bi[i][j]], inls[i] - 1, dis[i] + ci[i][j] + die[bi[i][j]]);
    dfs_seg(1, 1, tot);
     
    scanf("%lld", &q);
    for (LL i = 1; i <= q; ++ i)
    {
        LL a, b;
        scanf("%lld%lld", &a, &b);
        if (inls[a] > inls[b]) swap(a, b);
        if (lst[inls[a]] == a && lst[inls[b]] == b && inls[b] - inls[a] == 1)
            if (finmn[inls[a]] != INF) printf("%lld\n", finmn[inls[a]]);
            else puts("Infinity");
        else printf("%lld\n", dis[e]);
    }
}
View Code

 

呼~~~

posted @ 2016-10-25 18:51  AwD!  阅读(784)  评论(3编辑  收藏  举报