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 }
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 }
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 }
好像直接暴力就行了?
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 }
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 }
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 }
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 }
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 }
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 }
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 }