• CCPC-Wannafly Winter Camp Day1 (Div2, onsite)


    Replay


    Dup4:

    • 要是不挂机,再多仔细想想就好了
    • J确实自闭好久,一直在想正确性,最后数据错了,喵喵喵?
    • 还是要保证充足的休息啊,中间睡了一小会儿,也不知道睡了多久,醒来他们就又过了一道
    • 要发掘题目更多的性质和有用条件啊,算法和数据结构只是工具,不要总想着这是啥题这是啥题,我会不会,其实我啥都不会

    X:

    • 日常挂机时间久,感觉是个不好的习惯。
    • 太久没写了,已经不会算复杂度了, TLE MLE到自闭,转身写了个dp就过了?
    • 感觉太容易根据数据想算法了, 自导自演。
    • 自导自演,顺便演了16的C,演技拉满

    A:机器人

    Upsolved.

    思路:

    考虑两种情况

    第一种是$b区没有站点需要经过,并且在a区只有一侧有站点需要经过的话$

    我们不需要走一个圈,直接从$s走出去到一个特殊点走回s即可$

    还有一种就是走一个矩形

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define INF 0x3f3f3f3f
     6 #define INFLL 0x3f3f3f3f3f3f3f3f
     7 int n, r, m, k, s;
     8 vector <int> v[2], p; 
     9 
    10 int main()
    11 {
    12     while (scanf("%d%d%d%d%d", &n, &r, &m, &k, &s) != EOF)
    13     {
    14         v[0].clear(); v[1].clear(); p.clear();
    15         for (int i = 1, x, y; i <= r; ++i)
    16         {
    17             scanf("%d%d", &x, &y);
    18             v[y].push_back(x);
    19         }
    20         sort(v[0].begin(), v[0].end());
    21         sort(v[1].begin(), v[1].end());  
    22         for (int i = 1, x; i <= m; ++i)
    23         {
    24             scanf("%d", &x);
    25             p.push_back(x);
    26         }    
    27         p.push_back(1); p.push_back(n); 
    28         sort(p.begin(), p.end());
    29         p.erase(unique(p.begin(), p.end()), p.end());  
    30         int Max = 0; 
    31         int Min = INF;  
    32         if (!v[0].empty()) 
    33         {
    34             Max = max(Max, *v[0].rbegin()); 
    35             Min = min(Min, *v[0].begin());
    36         }
    37         if (!v[1].empty())
    38         {
    39             Max = max(Max, *v[1].rbegin());
    40             Min = min(Min, *v[1].begin()); 
    41         }
    42         int pos_Max = lower_bound(p.begin(), p.end(), Max) - p.begin();
    43         int pos_Min = upper_bound(p.begin(), p.end(), Min) - p.begin() - 1; 
    44         pos_Max = p[pos_Max];
    45         pos_Min = p[pos_Min];  
    46         ll res = INFLL;
    47         // b区不需要经过站点
    48         if (v[1].empty())
    49         {
    50             if (Max <= s) pos_Max = s;  
    51             if (Min >= s) pos_Min = s;  
    52             ll tmp = 2ll * (pos_Max - pos_Min);
    53             res = min(res, tmp);
    54         }
    55         // 走矩形路线
    56         if (!v[1].empty())  
    57         {
    58             ll tmp = 2ll * (pos_Max - pos_Min);
    59             if (!v[1].empty()) tmp += 2ll * k; 
    60             if (s < pos_Min)  
    61                 tmp += 2ll * (pos_Min - s);
    62             if (s > pos_Max)
    63                 tmp += 2ll * (s - pos_Max);
    64             res = min(res, tmp); 
    65         }    
    66         printf("%lld
    ", res);  
    67     }
    68     return 0;
    69 }
    View Code

    B:吃豆豆

    Solved.

    思路:

    $dp[i][j][k]表示在(i, j)位置, 时间为t获得最多的糖果。$

    $那么, 很显然有转移方程$

    $dp[i][j][k] -> dp[i +1][j][k + 1]$

    $dp[i][j][k] -> dp[i - 1][j][k + 1]$

    $dp[i][j][k] -> dp[i][j + 1][k + 1]$

    $dp[i][j][k] -> dp[i][j - 1][k + 1]$

    $dp[i][j][k] -> dp[i][j][k + 1]$

    $当时间k\%T[i][j] == 0时, dp[i][j][k]++$

    $一共有n*m*c种状态, 复杂度是O(n*m*c)$

    $然后我也不知道我为啥写了个滚动数组$

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 const int maxn = 10 + 10;
     6 
     7 int n, m, C;
     8 int sx, sy, ex, ey;
     9 int T[maxn][maxn];
    10 int arr[maxn][maxn];
    11 int brr[maxn][maxn];
    12 
    13 int main()
    14 {
    15     while(~scanf("%d %d %d", &n, &m, &C))
    16     {
    17         for(int i = 1; i <= n; ++i)
    18         {
    19             for(int j = 1; j <= m; ++j)
    20             {
    21                 scanf("%d", &T[i][j]);
    22             }
    23         }
    24         scanf("%d %d %d %d", &sx, &sy, &ex, &ey);
    25         memset(arr, -1, sizeof arr);
    26         arr[sx][sy] = 0;
    27         int step = 1;
    28         for(step = 1; ; step++)
    29         {
    30             for(int i = 1; i <= n; ++i)
    31             {
    32                 for(int j = 1; j <= m; ++j)
    33                 {
    34                     brr[i][j] = arr[i][j];
    35                 }
    36             }
    37             for(int i = 1; i <= n; ++i)
    38             {
    39                 for(int j = 1; j <= m; ++j)
    40                 {
    41                     if(i - 1 >= 1) arr[i][j] = max(arr[i][j], brr[i - 1][j]);
    42                     if(i + 1 <= n) arr[i][j] = max(arr[i][j], brr[i + 1][j]);
    43                     if(j - 1 >= 1) arr[i][j] = max(arr[i][j], brr[i][j - 1]);
    44                     if(j + 1 <= m) arr[i][j] = max(arr[i][j], brr[i][j + 1]);
    45                     if(step % T[i][j] == 0)
    46                     {
    47                         if(arr[i][j] >= 0) arr[i][j]++;
    48                     }
    49                 }
    50             }
    51             if(arr[ex][ey] >= C) break;
    52         }
    53         printf("%d
    ", step);
    54     }
    55     return 0;
    56 }
    View Code

    C:拆拆拆数

    Solved.

    思路:

    对于两个奇数,因为2和任意奇数都是互质的。

    所以可以拆成A(2,A-2),B(B-2,2)

    当有偶数的时候,假设A是偶数

    对于大于4的偶数,一定可以拆成一个奇质数和一个奇数之和,相应的将另一个数拆成一个2对应之前的奇数,剩下的只要做到与奇质数互质即可。

    因为3*5*7*11*13*17*19*23*29*31*37*41*43*47*53>1e9,所以B-2在53之前一定有一个奇质数不是B-2的因子。

      1 #include<bits/stdc++.h>
      2 
      3 using namespace std;
      4 
      5 typedef long long ll;
      6 
      7 ll GCD(ll a, ll b)
      8 {
      9     return b == 0 ? a : GCD(b, a % b);
     10 }
     11 
     12 ll a, b;
     13 ll prime[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};//15
     14 
     15 
     16 int main()
     17 {
     18     int t;
     19     scanf("%d", &t);
     20     while(t--)
     21     {
     22         scanf("%lld %lld", &a, &b);
     23         if(GCD(a, b) == 1)
     24         {
     25             puts("1");
     26             printf("%lld %lld
    ", a, b);
     27             continue;
     28         }
     29         if(a == b)
     30         {
     31             if(a & 1)
     32             {
     33                 ll ans1 = a / 2;
     34                 ll ans2 = a - a / 2;
     35                 puts("2");
     36                 printf("%lld %lld
    ", ans1, ans2);
     37                 printf("%lld %lld
    ", ans2, ans1);
     38             }
     39             else 
     40             {
     41                 ll ans1 = a / 2 - 1;
     42                 ll ans2 = a - ans1;
     43                 puts("2");
     44                 printf("%lld %lld
    ", ans1, ans2);
     45                 printf("%lld %lld
    ", ans2, ans1);
     46             }
     47             continue;
     48         }
     49         if(a % 2 == 1 && b % 2 == 1)
     50         {
     51             ll ans1 = 2, ans2 = a - 2;
     52             ll ans3 = b - 2, ans4 = 2;
     53             puts("2
    ");
     54             printf("%lld %lld
    ", ans1, ans3);
     55             printf("%lld %lld
    ", ans2, ans4);
     56         }
     57         else
     58         {
     59             int isswap = 0;
     60             if(b % 2 == 0)
     61             {
     62                 swap(a, b);
     63                 isswap = 1;
     64             }
     65             int flag = 0;
     66             ll ans1, ans2, ans3, ans4;
     67             ans3 = b - 2;
     68             ans4 = 2;
     69             for(int i = 1; i <= 15 && prime[i] + 2 <= a; ++i)
     70             {
     71                 if((b - 2) % prime[i] != 0)
     72                 {
     73                     flag = 1;
     74                     ans1 = prime[i];
     75                     ans2 = a - prime[i];
     76                     break;
     77                 }
     78             }
     79             if(flag)
     80             {
     81                 if(isswap) 
     82                 {
     83                     swap(ans1, ans3);
     84                     swap(ans2, ans4);
     85                 }
     86                 puts("2");
     87                 printf("%lld %lld
    ", ans1, ans3);
     88                 printf("%lld %lld
    ", ans2, ans4);
     89                 continue;
     90             }
     91             ans3 = b - b % a - 1;
     92             ans4 = b - ans3;
     93             for(int i = 0; i <= 15 && prime[i] + 2<= a; ++i)
     94             {
     95                 ans1 = prime[i];
     96                 ans2 = a - prime[i];
     97                 if(GCD(ans1, ans3) == 1 && GCD(ans2, ans4) == 1)
     98                 {
     99                     flag = 1;
    100                     break;
    101                 }
    102             }
    103             if(flag)
    104             {
    105                 if(isswap)
    106                 {
    107                     swap(ans1, ans3);
    108                     swap(ans2, ans4);
    109                 }
    110                 puts("2");
    111                 printf("%lld %lld
    ", ans1, ans3);
    112                 printf("%lld %lld
    ", ans2, ans4);
    113             }
    114             else
    115             {
    116                 puts("-1");
    117             }
    118         }
    119     }
    120     return 0;
    121 }
    View Code

    好像直接暴力就行了?

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 int t; ll a, b;
     6 
     7 ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
     8 void solve()
     9 {
    10     for (int i = 2; i <= min(a, 1ll * 100); ++i) for (int j = 2; j <= min(b, 1ll * 100); ++j) if (gcd(i, j) == 1 && gcd(a - i, b - j) == 1)
    11     {
    12         puts("2");
    13         printf("%d %d
    ", i, j);
    14         printf("%lld %lld
    ", a - i, b - j);
    15         return;
    16     }
    17 }
    18 
    19 int main()
    20 {
    21     scanf("%d", &t);
    22     while (t--)
    23     {
    24         scanf("%lld%lld", &a, &b);
    25         if (gcd(a, b) == 1) 
    26         {
    27             puts("1");
    28             printf("%lld %lld
    ", a, b);
    29         }
    30         else solve();
    31     }
    32     return 0;
    33 }
    View Code

    E:流流流动

    Upsolved.

    这些边连起来会形成一棵树、简单树形dp即可

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define N 210
     5 int n, f[N], d[N];
     6 vector <int> G[N];
     7 
     8 int pre[N];
     9 int find(int x) { return pre[x] == 0 ? x : pre[x] = find(pre[x]); }
    10 void join(int x, int y)
    11 {
    12     int fx = find(x), fy = find(y);
    13     if (fx != fy)
    14         pre[fx] = fy;
    15 }
    16 
    17 int dp[N][2];
    18 void DFS(int u, int fa)
    19 {
    20     dp[u][0] = 0;
    21     dp[u][1] = f[u];
    22     for (auto v : G[u]) if (v != fa)
    23     {
    24         DFS(v, u);
    25         dp[u][0] += max(dp[v][0], dp[v][1]);
    26         dp[u][1] += max(dp[v][0], dp[v][1] - d[min(u, v)]);
    27     }
    28 }
    29 
    30 int main()
    31 {
    32     while (scanf("%d", &n) != EOF)
    33     {
    34         for (int i = 0; i <= n; ++i) G[i].clear();
    35         memset(pre, 0, sizeof pre);
    36         memset(dp, 0, sizeof dp); 
    37         for (int i = 1; i <= n; ++i) scanf("%d", f + i);
    38         for (int i = 1; i <= n; ++i) scanf("%d", d + i); 
    39         for (int i = 2; i <= n; ++i)
    40         {
    41             if (i & 1)
    42             {
    43                 if (3 * i + 1 <= n)
    44                 {
    45                     G[3 * i + 1].push_back(i);
    46                     G[i].push_back(3 * i + 1);
    47                     join(3 * i + 1, i);            
    48                 }
    49             }
    50             else
    51             {
    52                 G[i].push_back(i / 2);
    53                 G[i / 2].push_back(i); 
    54                 join(i, i / 2);
    55             }
    56         }
    57         for (int i = 1; i <= n; ++i) if (!pre[i])
    58         {
    59             G[0].push_back(i); 
    60             G[i].push_back(0);
    61         }
    62         DFS(0, 0); 
    63         printf("%d
    ", max(dp[0][0], dp[0][1]));
    64     }
    65     return 0;
    66 }
    View Code

    F:爬爬爬山

    Solved.

    思路:

    考虑海拔和体力的关系,即最高可以爬的山为$h[1] + k$

    那么对于其他高度不符合的山,我们就需要降低其高度

    可以理解为点有点权,边有边权,经过边和点都要加上其权值,求最短路

    将一个点拆成两个点,中间连条有向边,权值为该点点权,再跑最短路即可

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define N 200010
     6 #define INFLL 0x3f3f3f3f3f3f3f3f
     7 int n, m, k, h[N];
     8 ll d;
     9 struct Graph
    10 {
    11     struct node
    12     {
    13         int to, nx; ll w;
    14         node () {}
    15         node (int to, int nx, ll w) : to(to), nx(nx), w(w) {}
    16     }a[N << 3];
    17     int head[N], pos;
    18     void init()
    19     {
    20         memset(head, 0, sizeof head);
    21         pos = 0;
    22     }
    23     void add(int u, int v, ll w)
    24     {
    25         a[++pos] = node(v, head[u], w); head[u] = pos;
    26     }
    27 }G;
    28 #define erp(u) for (int it = G.head[u], v = G.a[it].to; it; it = G.a[it].nx, v = G.a[it].to)
    29 
    30 struct node
    31 {
    32     int to; ll w;
    33     node () {}
    34     node (int to, ll w) : to(to), w(w) {}
    35     bool operator < (const node &other) const { return w > other.w; }
    36 };
    37 ll dist[N]; bool used[N];
    38 void Dijkstra()
    39 {
    40     for (int i = 1; i <= 2 * n + 1; ++i) dist[i] = INFLL, used[i] = false;
    41     dist[2] = 0; priority_queue <node> pq; pq.push(node(2, 0));
    42     while (!pq.empty())
    43     {
    44         int u = pq.top().to; pq.pop();
    45         if (used[u]) continue;
    46         used[u] = true;
    47         erp(u) 
    48         {
    49             ll w = G.a[it].w;
    50             if (!used[v] && dist[v] > dist[u] + w)
    51             {
    52                 dist[v] = dist[u] + w;
    53                 pq.push(node(v, dist[v]));
    54             }
    55         }
    56     }
    57 }
    58 
    59 int main()
    60 {
    61     while (scanf("%d%d%d", &n, &m, &k) != EOF)
    62     {
    63         G.init();  
    64         for (int i = 1; i <= n; ++i) scanf("%d", h + i);
    65         d = h[1] + k;
    66         for (int i = 1, u, v, w; i <= m; ++i) 
    67         {
    68             scanf("%d%d%d", &u, &v, &w);
    69             G.add(2 * u + 1, 2 * v, w);
    70             G.add(2 * v + 1, 2 * u, w);
    71         }
    72         for (int i = 1; i <= n; ++i)
    73         {
    74             ll w = 0;
    75             if (h[i] > d) w = 1ll * (h[i] - d) * (h[i] - d);
    76             G.add(2 * i, 2 * i + 1, w);
    77         }
    78         Dijkstra();
    79         printf("%lld
    ", dist[2 * n + 1]);
    80     }
    81     return 0;
    82 }
    View Code

    I:起起落落

    Upsolved.

    思路:

    $f[i] 表示以i结尾的方案数$ 

    $f[i] = sumlimits_{j = 1}^{i - 1} [p[j] > p[i]] cdot f[j] cdot sumlimits_{k = j + 1}^{i - 1} [p[k] < p[i]]$

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define N 2010
     6 const ll MOD = (ll)1e9 + 7;
     7 int n, p[N];
     8 ll f[N];
     9 
    10 int main()
    11 {
    12     while (scanf("%d", &n) != EOF)
    13     {
    14         for (int i = 1; i <= n; ++i) scanf("%d", p + i); 
    15         memset(f, 0, sizeof f);
    16         for (int i = 3; i <= n; ++i)
    17         {
    18             int cnt = p[i - 1] < p[i]; 
    19             for (int j = i - 2; j >= 1; --j)   
    20             {
    21                 if (p[j] > p[i]) 
    22                     f[i] = (f[i] + (f[j] + 1) * cnt % MOD) % MOD;
    23                 else
    24                     ++cnt; 
    25             }
    26         }
    27         ll res = 0;
    28         for (int i = 3; i <= n; ++i) res = (res + f[i]) % MOD; 
    29         printf("%lld
    ", res);
    30     }
    31     return 0;
    32 }
    View Code

    Div1:

    用权值线段树来维护这个式子

    我们考虑$f[x]的方案只会对 y > x ;并且; p[y] < p[x] 的y产生贡献$

    产生贡献的次数是$z in [x + 1, y - 1] 中 p[z] < p[x] 的个数$

    我们对于$z in [x + 1, y - 1] 中,不管p[z] 与 p[x] 的大小关系$

    全都把次数算上

    再考虑删除多算的贡献

    对于$z in [x + 1, y - 1] 它多产生的贡献是 x in [1, z] ;并且; p[x] > p[z] 的 f[x] 之和$

    $那就是权值线段树上的 区间更新 单点更新 区间查询的操作$

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define N 100010
     6 const ll MOD = (ll)1e9 + 7;
     7 int n, p[N];
     8 
     9 void up(ll &x) { if (x >= MOD) x -= MOD; }
    10 namespace SEG
    11 {
    12     struct node
    13     {
    14         ll base, v, lazy;
    15         node () {}
    16         node (ll base, ll v, ll lazy) : base(base), v(v), lazy(lazy) {}
    17         void init() { base = v = lazy = 0; }
    18         node operator + (const node &other) const
    19         {
    20             node res; res.init();
    21             res.base = (base + other.base) % MOD;
    22             res.v = (v + other.v) % MOD; 
    23             return res;
    24         }    
    25     }a[N << 2], res;
    26     void init() { memset(a, 0, sizeof a); }
    27     void pushdown(int id)
    28     {
    29         if (!a[id].lazy) return;
    30         up(a[id << 1].lazy += a[id].lazy);
    31         up(a[id << 1 | 1].lazy += a[id].lazy);
    32         a[id << 1].v = (a[id << 1].v + a[id].lazy * a[id << 1].base % MOD) % MOD;
    33           a[id << 1 | 1].v = (a[id << 1 | 1].v + a[id].lazy * a[id << 1 | 1].base % MOD) % MOD;
    34         a[id].lazy = 0;    
    35     }
    36     void update(int id, int l, int r, int ql, int qr, ll v)
    37     {
    38         if (l >= ql && r <= qr)
    39         {
    40             up(a[id].lazy += v);
    41             a[id].v = (a[id].v + a[id].base * v % MOD) % MOD;
    42             return;
    43         }
    44         int mid = (l + r) >> 1;
    45         pushdown(id);
    46         if (ql <= mid) update(id << 1, l, mid, ql, qr, v);
    47         if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, v);
    48         a[id] = a[id << 1] + a[id << 1 | 1];
    49     }
    50     void update2(int id, int l, int r, int pos, ll base, ll v)
    51     {
    52         if (l == r)
    53         {
    54             up(a[id].base += base);
    55             up(a[id].v += v);
    56             return;
    57         }
    58         int mid = (l + r) >> 1;
    59         pushdown(id);
    60         if (pos <= mid) update2(id << 1, l, mid, pos, base, v);
    61         else update2(id << 1 | 1, mid + 1, r, pos, base, v);
    62         a[id] = a[id << 1] + a[id << 1 | 1];
    63     }
    64     void query(int id, int l, int r, int ql, int qr)
    65     {
    66         if (l >= ql && r <= qr)
    67         {
    68             res = res + a[id];
    69             return;
    70         }
    71         int mid = (l + r) >> 1;
    72         pushdown(id); 
    73         if (ql <= mid) query(id << 1, l, mid, ql, qr);
    74         if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr);
    75     }
    76 }
    77 
    78 int main()
    79 {
    80     while (scanf("%d", &n) != EOF)
    81     {
    82         for (int i = 1; i <= n; ++i) scanf("%d", p + i);
    83         SEG::init();
    84         ll res = 0;
    85         for (int i = 1; i <= n; ++i)
    86         {
    87             SEG::res.init();
    88             SEG::query(1, 1, n, p[i] + 1, n); 
    89             up(res += SEG::res.v);
    90             SEG::update(1, 1, n, p[i] + 1, n, 1); 
    91             SEG::update2(1, 1, n, p[i], SEG::res.v + 1, (-SEG::res.base + MOD) % MOD); 
    92         }
    93         printf("%lld
    ", res);   
    94     }
    95     return 0;
    96 }
    View Code

    J:夺宝奇兵

    Solved.

    思路:

    考虑居民拥有的最大宝物数为$m$

    可以枚举这个值,贪心$check()$即可

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define N 1010
     6 int n, m;
     7 vector <int> v[N];
     8 
     9 ll check(int x)
    10 {
    11     ll res = 0;
    12     int cnt = 0;
    13     priority_queue <int, vector <int>, greater <int> > q;
    14     for (int i = 1; i <= n; ++i)
    15     {
    16         int need = v[i].size() - x;
    17         for (auto it : v[i])
    18         {
    19             if (need > 0)
    20             {
    21                 --need;
    22                 res += it;
    23                 ++cnt;
    24             }
    25             else 
    26                 q.push(it);
    27         }
    28     }
    29     while (cnt <= x)  
    30     {
    31         ++cnt;
    32         res += q.top(); q.pop();    
    33     }
    34     return res;
    35 }
    36 
    37 int main()
    38 {
    39     while (scanf("%d%d", &n, &m) != EOF)
    40     {
    41         for (int i = 1; i <= n; ++i) v[i].clear();
    42         for (int i = 1, a, c; i <= m; ++i) 
    43         {
    44             scanf("%d%d", &a, &c);
    45             v[c].push_back(a);
    46         }
    47         for (int i = 1; i <= n; ++i) sort(v[i].begin(), v[i].end());
    48         ll res = (ll)1e18; 
    49         for (int i = 0; i < m; ++i) 
    50             res = min(res, check(i));
    51         printf("%lld
    ", res);
    52     }
    53     return 0;
    54 }
    View Code

    Div1:

    从大到小枚举$x$, 发现在大的$x的情况下会买掉的,在小的x下一定会买掉$

    $每件物品最多操作一次,再用线段树维护前k小之和即可$

    复杂度$O(nlogn)$

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 
      4 #define ll long long
      5 #define N 100010
      6 #define pii pair <int, int>
      7 int n, m;
      8 struct node
      9 {
     10     int a, c;
     11     void scan() { scanf("%d%d", &a, &c); }
     12     bool operator < (const node &other) const { return a < other.a; }
     13 }arr[N];
     14 queue <pii> q[N];
     15 vector <int> vec[N];
     16 
     17 namespace SEG
     18 {
     19     ll v[N << 2]; int cnt[N << 2];
     20     void pushup(int id) { v[id] = v[id << 1] + v[id << 1 | 1]; cnt[id] = cnt[id << 1] + cnt[id << 1 | 1]; }
     21     void build(int id, int l, int r)
     22     {
     23         v[id] = 0; cnt[id] = 0;
     24         if (l == r)
     25         {
     26             v[id] = arr[l].a;
     27             cnt[id] = 1;
     28             return;
     29         }
     30         int mid = (l + r) >> 1;
     31         build(id << 1, l, mid);
     32         build(id << 1 | 1, mid + 1, r);
     33         pushup(id);
     34     }
     35     void update(int id, int l, int r, int pos)
     36     {
     37         if (l == r)
     38         {
     39             v[id] = 0;
     40             cnt[id] = 0;
     41             return;
     42         }
     43         int mid = (l + r) >> 1;
     44         if (pos <= mid) update(id << 1, l, mid, pos);
     45         else update(id << 1 | 1, mid + 1, r, pos);
     46         pushup(id);
     47     }
     48     ll query(int id, int l, int r, int k)
     49     {
     50         if (k <= 0) return 0;
     51         if (k == cnt[id]) return v[id];
     52         int mid = (l + r) >> 1;
     53         ll res = 0;
     54         res += query(id << 1, l, mid, min(k, cnt[id << 1]));
     55         if (k - cnt[id << 1] > 0) res += query(id << 1 | 1, mid + 1, r, k - cnt[id << 1]);
     56         return res;
     57     }
     58 }
     59 
     60 ll tmp;
     61 int cnt;
     62 void work(int x)
     63 {
     64     if (x - 1) for (auto it : vec[x]) vec[x - 1].push_back(it);
     65     for (auto it : vec[x]) 
     66     {
     67         pii top = q[it].front(); q[it].pop();
     68         ++cnt;
     69         tmp += top.first;
     70         SEG::update(1, 1, m, top.second);    
     71     }
     72 }
     73 
     74 int main()
     75 {
     76     while (scanf("%d%d", &n, &m) != EOF)
     77     {
     78         for (int i = 1; i <= m; ++i) arr[i].scan();
     79         sort(arr + 1, arr + 1 + m);
     80         for (int i = 1; i <= m; ++i) q[arr[i].c].push(pii(arr[i].a, i)); 
     81         SEG::build(1, 1, m);
     82         for (int i = 1; i <= n; ++i) vec[q[i].size()].push_back(i);
     83         ll res = (ll)1e18; tmp = 0; cnt = 0;
     84         if (!vec[m].empty())
     85         {
     86             ++cnt;
     87             int it = *vec[m].begin();
     88             pii top = q[it].front(); q[it].pop();
     89             vec[m - 1].push_back(it); 
     90             tmp += top.first;
     91             SEG::update(1, 1, m, top.second); 
     92         }
     93         for (int i = m - 1; i >= 0; --i)
     94         {
     95             work(i);
     96             res = min(res, tmp + SEG::query(1, 1, m, i - cnt));
     97         }    
     98         printf("%lld
    ", res);
     99     }    
    100     return 0;
    101 }
    View Code
  • 相关阅读:
    kettle部分传输场景应用(每个作业都实验过啦)
    Java设计模式之《适配器模式》及应用场景
    Mysql笔记
    Spring知识点
    Java基础系列-浅拷贝和深拷贝
    前端-javascript知识点
    前端-jquery知识点
    Java基础系列-substring的原理
    Java设计模式之《抽象工厂模式》及使用场景
    Java基础系列-Enum深入解析
  • 原文地址:https://www.cnblogs.com/Dup4/p/10296448.html
Copyright © 2020-2023  润新知