zoukankan      html  css  js  c++  java
  • 8.7集训

    上午

    考试

    第一题

    (T)次询问,每次询问给定(n),求 (sum_limits{i=1}^n lfloor dfrac n i floor)

    上来看到(2s;1GB),直接准备打表…..

    于是我开始手算…..

    (n = 1get1,n=2get3,n=3get5,n=4get8,n=5get10,n=6get14,n=7get16,)

    (n=8get20,n=9get23)

    我注意到,(n)为奇数的时候,比她小的偶数的(ans_{i-1})就为(ans_i-2)

    于是我开始验证,嗯,(2,3)满足,(4,5)满足,(6,7)满足,我没有验证(8,9)

    可偏偏(8,9)就没有满足这个规律!

    于是在我自以为正确的规律下,开始计算打表时间,计算最多可以达到多少

    (1e7*1e7/1e9)需要三个多小时

    (1e6*1e6/1e9)需要四分钟

    但是(1e7)太诱人了,满分太诱人了,我又考虑各种优化打表程序

    直到最后放弃打表(1e7),但是为了(1e6)的打表程序,我还搞了搞常数优化

    (45mins ;later)

    我注意到(n =8get20),我仍旧以为是我手算错了,还在草稿纸上,用红笔改为了(21)

    交上去之后,首先是找不到源程序,搞得我一头雾水,后来把源程序的内存限制改为了(10000KB)

    (我的程序是(7000多KB)

    我的代码能编译了,结果一片红,那一刻,我心里是真的凉


    整除分块只有四十分

    考虑(lfloor dfrac ni floor)的含义:(1 ext~N)(i)的倍数的数目

    那么所求的就是(1 ext~N)中所有数的倍数的数目,反过来就是(1 ext~N)所有数的约数的数目

    所求转化为(sum_limits{i=1}^nd(i)),其中(d(i))(i)的约数的个数

    线性筛可以满分

    关于线性筛约数,我还不会,甩个链接click1click2

    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    
    const int Tn = 4e7+66;
    
    int N, T, p, cnt;
    int res, last = -2;
    int a[Tn], v[Tn], f[Tn];
    LL sum[Tn]; int prime[Tn];
    
    inline int thestars() {
       scanf ("%d%d%d", &N, &T, &p);
       f[1] = 1;
       for (int i = 2; i <= N; ++ i) {
          if (!v[i]) {
             a[i] = 1;
             f[i] = 2;
             prime[++ cnt] = i;
          }
          for (int j = 1; j <= cnt && i * prime[j] <= N; ++ j) {
             int now = i * prime[j];
             v[now] = 1;
             if (i % prime[j] == 0) {
                a[now] = a[i] + 1;
                f[now] = f[i]/(a[i]+1)*(a[i]+2);
                break;
             } else a[now] = 1, f[now] = f[i]*2;
          }
       }
       for (int i = 1; i <= N; ++ i) sum[i] = sum[i - 1] + f[i];
       for (int i = 1; i <= T; ++ i) res ^= (last = sum[(last+i+p)%N+1]);
       cout << res;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    第二题

    一句话题意:小伙子和小姑娘玩一个游戏:在一个装有(w)个白球,(b)个黑球的袋子里,轮流摸球,摸到一个就扔一个,谁先拿到白球谁赢。小伙子为了赢,于是作弊:自己拿完球之后,如果袋子里面还有球,他就再拿一个扔掉,无论这个是白球或者黑球,他第二次摸的这个不计输赢

    算小姑娘赢的概率

    (概率dp)

    (f[i][j])表示袋子里还有(i)个白球和(j)个黑球时,Alice获胜的概率

    则Alice获胜有三种情况:

    • 1.小姑娘直接摸出白球,概率(dfrac i {i+j})
    • 2.小姑娘运气不好,摸到了黑球,小伙子摸出了黑球,扔了一个白球
    • 这时候概率显然:(dfrac i {i+j} imes dfrac{j - 1} {i+j-1} imesdfrac i {i+j-2} imes f[i-1][j-2])
    • 3.小姑娘还是摸出黑球,小伙子摸出了一个黑球,扔掉了一个黑球
    • 这时候概率显然:(dfrac i {i+j} imes dfrac {j-1}{i+j-1} imes dfrac {j-2}{i+j-2} imes f[i][j-3])
    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    
    const int N = 1e3+66;
    
    int w, b;
    double f[N][N];
    
    inline int thestars() {
       scanf ("%d%d", &w, &b);
       for (int i = 1; i <= w; ++ i) f[i][0] = 1;
       for (int i = 0; i <= b; ++ i) f[0][i] = 0;
       for (int i = 1; i <= w; ++ i) {
          for (int j = 1; j <= b; ++ j) {
             f[i][j] += (double)i/(i + j);
             if (j > 1) {
                f[i][j] += f[i - 1][j - 2]*j/(i + j)*(j - 1)/(i + j - 1)*i/(i + j - 2);
                if (j > 2) f[i][j] += f[i][j - 3]*j/(i + j)*(j - 1)/(i + j - 1)*(j - 2)/(i + j -2);
             }
          }
       }
       printf ("%.10lf
    ", f[w][b]);
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    第三题

    一句话题意:有(n)个人要参观(m)个村庄,一开始每一个村庄都没有人,当一个人进入之后,会产生一个惊叹值,大小等于此人进去之后,该村庄的总人数

    现在有(k)次清空村庄的机会,清空操作只能在一个人进入村庄并且产生惊叹值之后进行,求最小的惊叹值总和

    数据范围与提示

    对于(40\%)的数据:(m = 1)

    对于(60\%)的数据:(nleq 1000)

    对于(80\%)的数据:(n leq 50000)

    对于(100\%)的数据:(1leq n leq 1e6, 1 leq m leq 100, 1 leq k leq 500)

    我觉得是一个很好的题,数据给出(m =1),部分(pts)的做法,可以很好的引申到正解

    (m=1)我们显然要将机会平均分配

    (calc(n,x))表示一个村庄内(n)个人平均分成(x)段的最小惊叹值(用(k)次机会,会将村庄分成(k+1)段)

    那么我们可以(O(1))求出(calc(n,x) = (n\%x) imes sum(lfloordfrac nx floor+1) + (x-n\%x) imes sum(lfloordfrac nx floor))

    其中(sum[x]=dfrac {x(x+1)} x)。比较科学的解释是:从(1)加到(x),每一个数的和

    (m eq 1)的时候,显然要用分组背包啊

    (f[i][j])表示前(i)个村庄使用(j)次机会的最小惊叹值

    则有(f[i][j]=Min(f[i][j], f[i-1][t]+calc(cnt[i],j-t+1)))

    其中(cnt[i])是桶,答案就是:(f[m][k])

    时间复杂度:(O(n+m imes k^2))

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66;
    
    int n, m, k;
    int tong[N];
    int f[110][600];
    
    inline int sum(int x) {return x*(x+1)/2;}
    
    inline int calc(int x, int y) {return (x%y)*sum(x/y+1) + (y-x%y)*sum(x/y);}
    
    inline int thestars() {
       cin >> n >> m >> k;
       for (int i = 1; i <= n; ++ i) {
          int x;
          scanf ("%lld", &x);
          ++ tong[x];
       }
       memset(f, 0x3f, sizeof f);
       f[0][0] = 0;
       for (int i = 1; i <= m; ++ i) {
          for (int j = 0; j <= k; ++ j) {
             for (int z = 0; z <= j; ++ z) {
                f[i][j] = min(f[i][j], f[i - 1][z]+calc(tong[i], j - z + 1));
             }
          }
       }
       cout << f[m][k];
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    下午

    讲课,主要讲的数据结构

    晚上

    做题,上例题

    疯狂的馒头

    click

    一句话题意:给你一些区间,让你在一些区间染色,输出最后的染色结果,数据量(1e6)

    正着来貌似不太容易,我们尝试想做概率dp一样,反着搞

    因为倒着搞的话,每个颜色就固定了,不会再被更新,这样一个位置最多只需要染一次,染过就不需要再染

    并查集的一个经典神助攻:删除一个数后快速找到它后面第一个没有删除的数(处理完一个位置后快速找打它后面第一个需要处理的位置)用并查集维护这个点往后最近的白色馒头是谁,然后时光倒流,直接往它的fa处跳就行了

    这样就能保证每个位置只被染一次,时间复杂度(O(n+m))

    这个神助攻经常被用来“每个位置最多只会被处理一次”和“标记”和“快速找到最近的满足条件的位置”,常和离线、预处理、“正难则反”等结合,非常腻害

    #include <bits/stdc++.h>
    #define LL long long
    // #define debug
    using namespace std;
    
    const int T = 1e7+66;
    
    int N, M, p, q;
    int a[T], fa[T];
    
    inline int Find(int x) {return tmp = fa[x]==x?fa[x]=x:fa[x]=Find(fa[x]);}
    
    inline int thestars() {
       cin >> N >> M >> p >> q;
       for (int i = 1; i <= N + 1; ++ i) fa[i] = i;
       for (int i = M; i; -- i){
          int l = ((LL)i * p + q)%N+1;
          int r = ((LL)i * q + p)%N+1;
          if (l > r) swap(l, r);
          for (int j = Find(l); j <= r; j = Find(j)){
             a[j] = i, fa[j] = Find(j+1);
          }
       }
       for (int i = 1; i <= N; ++ i) cout << a[i] << '
    ';
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    
    

    区改区查

    click

    具体分析详见7.29

    #include <bits/stdc++.h>
    #define lowbit(x) (x&-x)
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e6+66;
    
    int n, q;
    int sum1[N], sum2[N];
    
    inline void jia (int x, int val) {
       for (int i = x; i <= n; i += lowbit(i))
          sum1[i] += val, sum2[i] += val*x;
    }
    inline void qujianjia (int l, int r, int val) {jia(l, val), jia(r+1, -val);}
    inline int chaxun(int x) {
       int res(0);
       for (int i = x; i; i -= lowbit(i))
          res += (x + 1)*sum1[i] - sum2[i];
       return res;
    }
    inline int qujianchaxun(int l, int r) {return chaxun(r) - chaxun(l-1);}
    
    inline int thestars() {
       scanf ("%lld%lld", &n, &q);
       for (int i = 1; i <= n; ++ i) {
          int x;
          scanf ("%lld", &x);
          qujianjia(i, i , x);
       }
       for (int i = 1; i <= q; ++ i) {
          int opt, l, r, x;
          scanf ("%lld", &opt);
          if (opt&1) {
             scanf ("%lld%lld%lld", &l, &r, &x);
             qujianjia(l, r, x);
          } else {
             scanf ("%lld%lld", &l, &r);
             cout << qujianchaxun(l, r) << '
    ';
          }
       }
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    
    
  • 相关阅读:
    paip.云计算以及分布式计算的区别
    paip.索引的种类以及实现attilax 总结
    paip.分布式应用系统java c#.net php的建设方案
    paip.提升性能--多核编程中的java .net php c++最佳实践 v2.0 cah
    paip.中文 分词 ---paoding 3.1 的使用
    paip.2013年技术趋势以及热点 v2.0 cae
    paip.为什么使用多线程的原因.
    paip.提升性能--多核cpu中的java/.net/php/c++编程
    paip.重装系统需要备份的资料总结..v2.0 cad
    paip.禁用IKAnalyzer 的默认词库.仅仅使用自定义词库.
  • 原文地址:https://www.cnblogs.com/yszhyhm/p/13451996.html
Copyright © 2011-2022 走看看