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\n", 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\n", 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\n", 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\n", ans1, ans2);
 37                 printf("%lld %lld\n", ans2, ans1);
 38             }
 39             else 
 40             {
 41                 ll ans1 = a / 2 - 1;
 42                 ll ans2 = a - ans1;
 43                 puts("2");
 44                 printf("%lld %lld\n", ans1, ans2);
 45                 printf("%lld %lld\n", 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\n");
 54             printf("%lld %lld\n", ans1, ans3);
 55             printf("%lld %lld\n", 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\n", ans1, ans3);
 88                 printf("%lld %lld\n", 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\n", ans1, ans3);
112                 printf("%lld %lld\n", 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\n", i, j);
14         printf("%lld %lld\n", 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\n", 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\n", 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\n", dist[2 * n + 1]);
80     }
81     return 0;
82 }
View Code

 

 

I:起起落落

Upsolved.

思路:

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

$f[i] = \sum\limits_{j = 1}^{i - 1} [p[j] > p[i]] \cdot f[j] \cdot \sum\limits_{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\n", 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\n", 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\n", 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\n", res);
 99     }    
100     return 0;
101 }
View Code

 

posted @ 2019-01-20 22:25  Dup4  阅读(538)  评论(0编辑  收藏  举报