• 2022CCPC广州


    E. Elevator

    题意:有n个电梯, 建筑有m层。每一个电梯从小到大有一个1-n的编号和出发的时间ai。在除了第一层和第m层都有一个开关,可以使到达这里的电梯停留1ms,问你对于第i个编号的电梯,如果要让它第一个到达m层,需要按多少层的开关。(如果有电梯同时到达,算编号小的先到)

    1. 6 20
    2. 3 8 12 6 9 9
    3. 0
    4. 8
    5. -1
    6. 4
    7. 13
    8. 14

    思路:观察样例可以知道,编号为i的电梯的作为第一个到达m层的时间=ai - 在前面出发的电梯的时间aj, 且如果j < i,编号小,ans还得额外+1. 那

    那么就很简单了,我们只需要去维护一个数据结构,它支持记录当前位置前面的出发的电梯的总个数和总时间即可。

    那么直接用树状数组维护一下即可。c1维护前面编号小且aj小的电梯个数。c2维护前面编号小的电梯出发的时间。因为我们将出发时间从小到大排序了,所以对于编号比当前大的后缀的数,直接用总的减去前缀即可。

    1. #include
    2. using namespace std;
    3. #pragma GCC optimize(2)
    4. #pragma GCC optimize(3,"Ofast","inline")
    5. #define x first
    6. #define y second
    7. #define endl '\n'
    8. #define rep(i,a,n) for (int i = a; i < n; i ++ )
    9. #define repn(i,a,n) for (int i = a; i <= n; i ++ )
    10. #define pb push_back
    11. #define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    12. typedef long long ll;
    13. typedef unsigned long long ull;
    14. #define all(x) (x).begin(),(x).end()
    15. typedef pair<int,int> PII;
    16. ll lowbit(ll x) { return x & (- x); }
    17. ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
    18. const int mod = 1e9+7;
    19. // Binary Index Tree
    20. // Fenwick's Tree
    21. const int N = 500010;
    22. template<class T>
    23. struct BIT {
    24. T c[N];
    25. int size;
    26. void init(int s) {
    27. size = s;
    28. for (int i = 1; i <= s; i++) c[i] = 0;
    29. }
    30. T query(int x) { // 1 ... x
    31. //assert(x <= size);
    32. T s = 0;
    33. for (; x; x -= x & (-x)) {
    34. s += c[x];
    35. }
    36. return s;
    37. }
    38. void modify(int x, T s) { // a[x] += s
    39. assert(x != 0);
    40. for (; x <= size; x += x & (-x)) {
    41. c[x] += s;
    42. }
    43. }
    44. };
    45. BIT c1, c2;
    46. vector v;
    47. ll ans[N];
    48. int n, m, a[N];
    49. int main(){
    50. IOS;
    51. cin >> n >> m;
    52. repn(i, 1, n) cin >> a[i], v.pb({a[i], i});
    53. sort(all(v));
    54. c1.init(n);
    55. c2.init(n);
    56. ll s = 0;//s是记录和的
    57. rep(i, 0, n) {
    58. auto [val, pos] = v[i];
    59. ans[pos] += c1.query(pos) * (val + 1) - c2.query(pos);//前缀的
    60. ans[pos] += (i - c1.query(pos)) * val - (s - c2.query(pos));
    61. //第一个ans[pos]是查询前缀中的差值, aj - ai(i < j) = 前面比aj小的个数 * aj - 前面的和, 因为位置问题还要加1
    62. //第二个ans是记录后缀中的差值, 因为插入是一步步从小到大的,比它小的一定出现了,
    63. //用当前的个数 - 前缀的个数就是后缀中的数字个数 * val - (后缀的总和 = s - 前缀总和) s是记录当前未插入之前的和
    64. s += val;
    65. c1.modify(pos, 1);//当前位置++, 出现了1次
    66. c2.modify(pos, val);//当前位置的值是val
    67. }
    68. repn(i, 1, n) {
    69. if(ans[i] > m - 2) ans[i] = -1;
    70. cout << ans[i] << endl;
    71. }
    72. return 0;
    73. }

     H:GameX

    博弈论:其实我屁都不会
    Alice和Bob玩游戏, 定义Mex(s)是s中未出现过的最小自然数
    每次Alice和Bob可以向集合中放数,最后MEX(s)是偶数Alice就赢了, 反之Bob赢了。

    思路:因为要找最小的没出现过的数,要使一个数成为最终的判定数,小于他的数必须都出现过,
    所以我们无论怎么放,都等价于从最小的数开始逐个往大放,我们从0开始遍历。A的最优策略就是放偶数,B的最优策略就是放奇数,我们从0开始遍历每个没在集合中的数,然后让AB进行他们所有的K个回合后,再重新遍历,看哪个数第一个没有出现。模拟

    1. #include
    2. using namespace std;
    3. #pragma GCC optimize(2)
    4. #pragma GCC optimize(3,"Ofast","inline")
    5. #define x first
    6. #define y second
    7. #define endl '\n'
    8. #define rep(i,a,n) for (int i = a; i < n; i ++ )
    9. #define repn(i,a,n) for (int i = a; i <= n; i ++ )
    10. #define pb push_back
    11. #define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    12. typedef long long ll;
    13. typedef unsigned long long ull;
    14. #define all(x) (x).begin(),(x).end()
    15. typedef pair<int,int> PII;
    16. ll lowbit(ll x) { return x & (- x); }
    17. ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
    18. const int mod = 1e9+7;
    19. const int N = 200010;
    20. void solve() {
    21. int n, k; cin >> n >> k;
    22. map<int, int> mp;
    23. for (int i = 1; i <= n; i ++ ) {
    24. int x; cin >> x;
    25. mp[x] ++;
    26. }
    27. int now = 0;
    28. int opa = k, opb = k;
    29. while(opa || opb) {
    30. while(mp[now]) now ++;//找出不在集合中的第一个元素
    31. if(opb != 0 && now & 1) {
    32. opb --;
    33. mp[now] ++;
    34. }
    35. else if(opa != 0 && now % 2 == 0) {
    36. opa --;
    37. mp[now] ++;
    38. }
    39. now ++;
    40. }
    41. now = 0;
    42. while(mp[now]) now ++;//找出第一个不在集合中的元素
    43. if(now & 1) puts("Bob");
    44. else puts("Alice");
    45. }
    46. int main(){
    47. IOS;
    48. int t;
    49. cin >> t;
    50. while(t -- ){
    51. solve();
    52. }
    53. return 0;
    54. }

     L. Station of Fate

    有n个人,m个车站,问你有多少种分配方式,保证每个车站必须有人。

    思路:他妈的插板法。要把n个人分成m组,因为有n-1个空挡,我们只需要在n-1个空档中插入m-1个板子就行了, 又因为题目规定不同的排列算不同,所以再×排列的个数n!,这就是答案了。

    记得算组合数Cn-1(m-1)时用一下费马小定理,将÷改成×。

    1. #include
    2. using namespace std;
    3. #pragma GCC optimize(2)
    4. #pragma GCC optimize(3,"Ofast","inline")
    5. #define x first
    6. #define y second
    7. #define endl '\n'
    8. #define rep(i,a,n) for (int i = a; i < n; i ++ )
    9. #define repn(i,a,n) for (int i = a; i <= n; i ++ )
    10. #define pb push_back
    11. #define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    12. typedef long long ll;
    13. typedef unsigned long long ull;
    14. #define all(x) (x).begin(),(x).end()
    15. typedef pair<int,int> PII;
    16. #define int long long
    17. ll lowbit(ll x) { return x & (- x); }
    18. ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
    19. const int mod = 998244353;
    20. const int N = 200010;
    21. int f[N];
    22. int qpow(int x) {
    23. int res = 1, b = mod - 2;
    24. while (b) {
    25. if (b & 1)
    26. res = res * x % mod;
    27. x = x * x % mod;
    28. b >>= 1;
    29. }
    30. return res;
    31. }
    32. void init() {
    33. f[0] = 1;
    34. for (int i = 1; i <= N; i ++ ) f[i] = (f[i - 1] * i) % mod;
    35. }
    36. int C(int a, int b) {
    37. if(b == 0) return 1;
    38. return 1ll * f[a] * qpow(f[b]) % mod * qpow(f[a - b]) % mod;//费马小定理
    39. }
    40. void solve() {
    41. int n, m; cin >> n >> m;
    42. cout << f[n] * C(n - 1, m - 1) % mod << endl;
    43. }
    44. signed main(){
    45. IOS;
    46. init();
    47. int t;
    48. cin >> t;
    49. while(t -- ){
    50. solve();
    51. }
    52. return 0;
    53. }
    54. /*
    55. where to find:
    56. 就是考虑n个人, 然后分成m组, 且n个人的顺序是可以不一样的
    57. 那么我们考虑一下插板法
    58. n个人有n-1个空挡, 只需要插入m-1块板子, 就可以分成m组, 然后因为n个人的顺序不一样,所以×n!
    59. */

  • 相关阅读:
    报错注入 [极客大挑战 2019]HardSQL1
    SAP官方免费提供的OData Gateway Demo 学习系统
    算法题:分发饼干
    HICO-DET:适合踏入 HOI detection 领域的初学者阅读的论文......
    通俗易懂介绍mysql索引为什么使用B+树?
    京东主图视频上传,如何关联商品投放?
    C++的关键字
    18 计专
    spring boot 配置文件
    “Life Long Learning”(终身学习)和“灾难性遗忘”(catastrophic forgetting)
  • 原文地址:https://blog.csdn.net/m0_61837058/article/details/127928740