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

    上午

    第一题

    其中(n leq 1e18)

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66;
    
    int n;
    
    inline int thestars() {
       cin >> n;
       int l = 1, r = sqrt(n)*2+1;
       int mo = 2*n-2;
       while (l < r) {
          int mid = (l + r)>>1;
          if (mid*(mid+1) >= mo) r = mid;
          else l = mid+1;
       }
       cout << l;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    第二题

    有百分之二十的暴力,但是需要LL

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    inline long long read() {
       long long s = 0, f = 1; char ch;
       while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
       for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
       return s * f;
    }
    
    const int N = 3e5+66, INF = 214748364000000;
    
    int n, res = INF;
    int vis[N];
    
    struct node{int a, t, v;}yhm[N];
    
    inline void dfs(int x, int num, int ans, int fangyu) {
       vis[x] = 1;
       if (num == n) {
          res = min(res, ans);
          return;
       }
       for (int i = 1; i <= n; ++ i) {
          if (!vis[i]) {
             int now_ans = (yhm[i].a-fangyu)*yhm[i].t+ans;
             int now_fangyu = yhm[i].v + fangyu;
             dfs(i, num+1, now_ans, now_fangyu);
             vis[i] = 0;
          }
       }
    }
    
    inline int thestars() {
       n = read();
       for (int i = 1; i <= n; ++ i)
          yhm[i].a = read(), yhm[i].t = read(), yhm[i].v = read();
       for (int i = 1; i <= n; ++ i) {
          memset (vis, 0, sizeof vis);
          dfs(i, 1, yhm[i].a*yhm[i].t, yhm[i].v);
       }
       cout << res;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    下面给出正解:

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    inline long long read() {
       long long s = 0, f = 1; char ch;
       while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
       for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
       return s * f;
    }
    
    const int N = 4e5+66;
    
    int n, res, fangyu;
    
    struct node {int a, t, v; double anzhi;}yhm[N];
    
    inline int cmp(node x, node y){return x.anzhi > y.anzhi;}
    
    inline int thestars() {
       cin >> n;
       for (int i = 1; i <= n; ++ i) {
          yhm[i].a = read(), yhm[i].t = read(), yhm[i].v = read();
          yhm[i].anzhi = (double)yhm[i].v/yhm[i].t;
       }
       sort (yhm+1, yhm+n+1, cmp);
       for (int i = 1; i <= n; ++ i) {
          res += (yhm[i].a - fangyu)*yhm[i].t;
          fangyu += yhm[i].v;
       }
       cout << res;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    第三题

    我的代码:

    #include <bits/stdc++.h>
    #define LL long long
    // #define debug
    using namespace std;
    
    const int N = 25;
    
    int n, m, q, res;
    int ql[N], qr[N], qs[N], qt[N], base[N];
    int dis[N][N], f[600000][N];
    
    inline void shuru() {
       cin >> n >> m >> q;
       memset(dis, 0x3f, sizeof dis);
       for (int i = 1; i <= n; ++ i) dis[i][i] = 0;
       for (int i = 1; i <= m; ++ i) {
          int u, v, w;
          cin >> u >> v >> w;
          dis[u][v] = min(dis[u][v], w);
       }
       for (int k = 1; k <= n; ++ k)
          for (int i = 1; i <= n; ++ i)
             for (int j = 1; j <= n; ++ j)
                dis[i][j] = min(dis[i][k]+dis[k][j], dis[i][j]);
       base[0] = 1;
       for (int i = 1; i <= q; ++ i) {
          cin >> qs[i] >> qt[i] >> ql[i] >> qr[i];
          base[i] = base[i - 1]*3;
       }
       memset(f, 0x3f, sizeof f); f[0][1] = 0;
    }
    
    inline void jiejue() {
       for (int i = 1; i < base[q]; ++ i) {
          for (int j = 1; j <= n; ++ j) {
             for (int k = 1; k <= q; ++ k) {
                if (i%base[k]/base[k - 1] == 1)//qu chu
                   f[i][qs[k]] = min(f[i][qs[k]],max((f[i - base[k - 1]][j] + dis[j][qs[k]]), ql[k]));
                else if (i%base[k]/base[k - 1] == 2 &&f[i - base[k - 1]][j] + dis[j][qt[k]] <= qr[k])
                   f[i][qt[k]] = min(f[i][qt[k]],f[i - base[k - 1]][j] + dis[j][qt[k]]);
             }
          }
       }
    }
    
    inline void shuchu() {
       for (int i = 0; i < base[q]; ++ i) {
          for (int j = 1; j <= n; ++ j) {
             if (f[i][j] != 0x3f3f3f3f) {
                int now(0);
                for (int k = 1; k <= q; ++ k)
                   if (i%base[k]/base[k - 1] == 2)
                      ++ now;
                      res = max(res, now);
             }
          }
       }
       cout << res;
    }
    
    inline int thestars() {
       shuru();
       jiejue();
       shuchu();
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    下午

    rqj学长讲课

    二分

    click

    我从来只写一种二分:

    二分
    while (l < r) {
       int mid = (l+r+1)>>1;
       if (a[mid] >= x) r = mid - 1;
       else l = mid;
    }
    

    二分有好多种写法,李某东dalao说,只有百分之十的程序员能写对二分。。。

    二分查找:在一个从小到大的序列中balabala...

    考虑:为什么一定是从小到大?因为二分无论是查找还是二分答案都必须利用序列的有序性

    有以下典型例题:

    挑石头

    click

    "最小距离最大值"典型二分答案,刚刚在rqj奆佬的指导下,改变了二分写法,这种二分结束后(l == r)(l,r)都是答案

    给出一组(Hack)数据:65343245 0 0

    (时隔半年,再写一次二分答案,感觉手生了好多,顿时又有些感动,我第一次学二分答案,到现在已经过去两三年了吧)

    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66;
    
    int n, m; LL L, res;
    int a[N];
    
    inline int pd(int x) {
       int an(0), last(0);
       for (int i = 1; i <= n+1; ++ i)
          (a[i]-last<x)? ++ an : last = a[i];
       // printf("pd %d = %d
    ", x, m >= an);
       return m>=an;
    }
    
    inline int thestars() {
       cin >> L >> n >> m;
       for (int i = 1; i <= n; ++ i) cin >> a[i];
       a[n + 1] = L;
       LL l = 1, r = L;
       while (l < r) {
          LL mid = (l+r+1)>>1;
          if (pd(mid)) l = mid;
          else r = mid - 1;
       }
       cout << l;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    自动做题机子

    click

    考虑二分答案,因为要求一个最小值和最大值嘛,跑两遍

    给出ac代码:

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66, inf = 1e18+250;
    
    int n, k, res_min = -1, res_max = -1;
    int a[N];
    
    inline int pd(int x) {
       int an(0), tmp(0);
       for (int i = 1; i <= n; ++ i) {
          tmp += a[i];
          if (tmp < 0) tmp = 0;
          if (tmp >= x) ++ an, tmp = 0; 
       }
       return an;
    }
    
    inline int thestars() {
       cin >> n >> k;
       for (int i = 1; i <= n; ++ i) cin >> a[i];
          int l = 1, r = inf;
          while (l < r) {
             int mid = (l+r) >> 1;
             if (pd(mid) <= k) {
                r = mid;
                if (pd(mid) == k) res_min = mid;
          }
          else l = mid + 1;
       }
       l = 0, r = inf;
       while (l < r) {
          int mid = (l+r+1) >> 1;
          if (pd(mid) >= k) {
             l = mid;
             if (pd(mid) == k) res_max = mid;
          }
          else r = mid-1;
       }
       if (res_min == -1) puts("-1");
       else cout << res_min << ' ' << res_max;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    注意:

    如果要二分答案求一个尽量小的值:

    find min
    int l = 1, r = inf;
    while (l < r) {
       int mid = (l+r)>>1;
       if (pd(mid)) r = mid;
       else l = mid+1;
    }
    

    如果二分答案求一个尽量大的值:

    find max
    int l = 1, r = inf;
    while (l < r) {
       int mid = (l+r+1)>>1;
       if (pd(mid)) l = mid;
       else r = mid - 1;
    }
    

    扑克

    click

    首先,我们可以发现,(J)(1,2,⋯ ,n)其实没什么区别,假如我们把 (J) 看成 (0) 号牌,那么,相当于这 (n+1) 种牌中任意 (n) 种各一张可以组成一套牌。

    然后,(n) 种各一张可以转化为 (n+1) 种各拿一张再取回去一张。

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66, inf = 600000000;
    
    int n, m;
    int a[N];
    
    inline int pd(int x) {
       int an(0);
       for (int i = 0; i <= n; ++ i)
          an += max(x - a[i], 0ll);
       return x>=an;
    }
    
    inline int thestars() {
       cin >> n >> a[0];
       for (int i = 1; i <= n; ++ i)
          cin >> a[i];
       int l = 0, r = inf;
       while (l < r) {
          int mid = (l+r+1)>>1;
          if(pd(mid)) l = mid;
          else r = mid - 1;
       }
       cout << l;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    在实数域的二分也有道题:

    极品飞车

    click

    已知(egin{aligned} sum_{i=1} ^n dfrac {d_i} {s_i+c} = tend{aligned})(c)((d_i>0, s_i+c>0))

    其中精度要求(1e)(-6)

    左边函数是单调递减的,因此可二分求解,

    常用板子:

    while (r - l > eps) {
        double mid = (l+r)/2;
        if (pd(mid)) l = mid;
        else r = mid;
    }
    其中eps = 1e-6
    

    小技巧:(while(cnt--))其中cnt = 100

    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66;
    const double eps = 1e-6;
    
    int n, t;
    int d[N];
    double s[N];
    
    inline int thestars() {
       while (scanf ("%d%d", &n, &t) != EOF) {
          int cnt = 100, i;
          double l = 1e8, r = 1e8, res, mid;
          for (i = 1; i <= n; ++ i) {
             cin >> d[i] >> s[i];
             l = min(l, s[i]);
          }
          l = -l;
          while (cnt --) {
             mid = (l+r)/2, res = 0;
             for (int i = 1; i <= n; ++ i) res += d[i]/(s[i]+mid);
             if (res < t) r = mid;
             else l = mid;
          }
          printf ("%.9lf
    ", l);
       }
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    关于分治,分而治之嘛

    有一些关于主定理的东西,但是noip不考,详见click

    无聊序列

    click

    考虑如果这个区间中存在某个数,使得它在整段区间中出现次数为1,那么对于所有包含该数的子区间,该数都出现且仅出现1次。所以只需要分治处理这个数左右两端的子区间即可;如果这个区间中不存在这样的数,那么它就是不合法的

    具体:从左右两端向中间扫,扫到了就停止。这样找一次的复杂度一定是2倍较短区间长度

    (20.08.10update)

    PS:题意太操蛋了,大致意思就是说,如果在某一个区间里面,发现某一个数出现了一次,那么这个区间就叫做不无聊的序列,

    比如({1,2,3,2,3})尽管2,3都出现了他娘的两次,但是1出现了一次,所以这个区间还是他娘的不无聊序列

    那我们就记这个数上一次出现的位置,和下一次出现的位置,特判(pres[linr] ; < ; l) && (nex[linr] ; > ; r)就好了

    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    //东方之珠 整夜未眠!
    const int N = 2e5+66;
    
    int a[N], pres[N], nex[N];
    map<int, int>mp;
    
    inline int work(int l, int r) {
       if (l == r || l > r) return 1;
       int linl = l, linr = r;
       while (linl < linr) {
          -- linr;
          if (pres[linr] < l && nex[linr] > r)
             if (work(l, linr - 1) && work(linr + 1, r))
                return 1;
          ++ linl;
          if (pres[linl] < l && nex[linl] > r)
             if (work(l, linl - 1) && work(linl + 1, r))
                return 1;
       }
       return 0;
    }
    
    inline int thestars() {
       int T, i, j, n;
       scanf ("%d", &T);
       while (T --> 0) {
          mp.clear();
          scanf ("%d", &n);
          for (i = 1; i <= n; ++ i) {
             scanf ("%d", &a[i]);
             pres[i] = 0, nex[i] = n+1;
          }
          for (int i = 1; i <= n; ++ i) {
             if (mp.find(a[i]) != mp.end()) {
                pres[i] = mp[a[i]];
                nex[mp[a[i]]] = i;
             }
             mp[a[i]] = i;
          }
          if (work(1, n)) puts("non-boring");
          else puts("boring");
       }
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    关于贪心嘛,一般都不是正解,但是应用面十分的广泛

    线段覆盖

    click

    右端点排个序就好了,证明也很简单,从左往右放,右端点越小,妨碍越小

    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    
    const int N = 1e5+66;
    
    int n, res;
    
    struct node {int s, t;}a[N];
    inline int cmp(node x, node y){return x.t < y.t;}
    
    inline int thestars() {
       scanf ("%d", &n);
       for (int i = 1; i <= n; ++ i)
          scanf ("%d%d", &a[i].s, &a[i].t);
       sort(a + 1, a + n + 1, cmp);
       int end = a[1].t; res = 1;
       for (int i = 2; i <= n; ++ i) {
          if (end <= a[i].s) {
             ++ res, end = a[i].t;
          }
       }
       cout << res;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    国王游戏

    click

    对于相邻的(i)(j),令(i,j)(j,i) 更优,

    则有(max(dfrac 1 {b[i]},dfrac {a[i]}{b[j]})<max(dfrac 1 {b[j]}, dfrac{a[j]} {b[i]}))

    又因为(dfrac {a[i]}{b[j]}>dfrac 1 {b[j]})(dfrac{a[j]} {b[i]}>dfrac 1 {b[i]})

    因此必须有(dfrac{a[j]} {b[i]} > dfrac {a[i]}{b[j]}>)化简后得(a[i]*b[i]<a[j]*b[j])

    于是贪心策略就是按照(a*b)从小到大排序,之后计算即可

    代码没写

    护花

    click

    一眼贪心,随便推一下就可以得到(dfrac {t[i]} {d[i]})

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e6+66;
    
    int n, sum[N];
    
    struct node {int t, d; double paixu;}a[N];
    
    inline int cmp(node x, node y) {return x.paixu < y.paixu;}
    
    inline int thestars() {
       cin >> n;
       for (int i = 1; i <= n; ++ i) {
          cin >> a[i].t >> a[i].d;
          a[i].paixu = (double)a[i].t/a[i].d;
       }
       sort(a + 1, a + n + 1, cmp);
       int res = 0, shijian = 0;
       for (int i = 2; i <= n; ++ i) {
          shijian += a[i - 1].t*2;
          res += shijian*a[i].d;
       }
       cout << res;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    贪心也经常与堆一起用

    合并果子

    click

    设有a、b、c,令a+b+a+b+c 是所有合并方案中最小的,推出a<c且b<c

    也可以用于“反悔“

    HUR-Warehouse Store

    click

    我们肯定是维护一个堆,如果发现当前的客户满足不了了,就尝试踢掉原来的最大的一个

    贪心证明:是当前的剩余尽量多,保证后面可以满足更多客户

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 3e5+66;
    
    int n, num;
    int a[N], b[N], v[N];
    
    priority_queue<pair<int, int> >q;
    
    inline int thestars() {
       scanf ("%lld", &n);
       for (int i = 1; i <= n; ++ i) scanf ("%lld", &a[i]);
       for (int i = 1; i <= n; ++ i) scanf ("%lld", &b[i]);
       int res = 0;
       for (int i = 1; i <= n; ++ i) {
          res += a[i];
          if (res >= b[i]) {
             res -= b[i];
             q.push(make_pair(b[i], i));
             v[i] = 1;
          } else {
             if (!q.empty() && q.top().first > b[i]) {
                int t = q.top().second; q.pop();
                res = res + b[t] - b[i];
                q.push(make_pair(b[i], i));
                v[t] = 0, v[i] = 1;
             }
          }
       }
       for (int i = 1; i <= n; ++ i) if (v[i]) ++ num;
       printf ("%lld
    ", num);
       for (int i = 1; i <= n; ++ i) 
          if (v[i])
             cout << i << ' ';
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    建筑抢修

    click

    可以推出按(second)从小到大排序,排完序之后和上个题一摸一样啊

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e6+66;
    
    priority_queue<int>q;
    
    int n, num;
    
    struct node {int s, t;}a[N];
    inline int cmp (node x, node y) {return x.t < y.t;}
    
    inline int thestars() {
       scanf ("%lld", &n);
       for (int i = 1; i <= n; ++ i)
          scanf ("%lld%lld", &a[i].s, &a[i].t);
       sort(a + 1, a + n + 1, cmp);
       int res = 0;
       for (int i = 1; i <= n; ++ i) {
          if (res + a[i].s <= a[i].t) {
             res += a[i].s;
             q.push(a[i].s);
             ++ num;
          } else if (!q.empty() && q.top() > a[i].s) {//为什么不写成res - q.top() + a[i].s <= a[i].t,留给读者自己思考
             res = res - q.top() + a[i].s; q.pop();
             q.push(a[i].s);
          }
       }
       cout << num;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    合并果子nb版本

    click

    考虑一个队列存排序结果(当然是桶排),另一个存合并结果,每次取出队首,这题需要快读和LL

    #include <bits/stdc++.h>
    #define int long long
    #define debug
    using namespace std;
    
    const int N = 1e7+66;
    
    queue<int>q1, q2;
    
    int n, res;
    int a[N], b[N>>6];
    
    inline int read() {
       int x = 0, f = 1; char ch;
       while (! isdigit(ch = getchar())) (ch == '-') && (f = -f);
       for (x = ch ^ 48; isdigit(ch = getchar()); x = (x << 3) + (x << 1) + (ch ^ 48));
       return x * f;
    }
    
    inline int Find() {
       int x;
       if ((q1.front() < q2.front() && !q1.empty()) || q2.empty()) {
          x = q1.front();
          q1.pop();
       } else {
          x = q2.front();
          q2.pop();
       }
       return x;
    }
    
    inline int thestars() {
       n = read();
       for (int i = 1; i <= n; ++ i) {
          a[i] = read();
          ++ b[a[i]];
       }
       for (int i = 1; i <= N>>6; ++ i) {
          while (b[i]) {
             -- b[i];
             q1.push(i);
          }
       }
       for (int i = 1; i < n; ++ i) {
          int x = Find();
          int y = Find();
          q2.push(x + y);
          res += x + y;
       }
       cout << res;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    还有一类比较难的数位贪心,整体思路是需要从高位到低位贪心,证明显然。这种题目常见与二进制位运算中

    起床困难综合症

    click

    位运算的主要特点是不用进位

    (x_0)的第k位应该填1而不是零,当且仅当满足一下两个条件:
    1.用已经填好的最高位的数加上当前的(1<<k)不超过m
    2.(lyd原话:)用每个参数的第k位参加运算,若初值为1,则n次运算之后结果为1
    若初值为0,则n次运算之后为0

    其实我现在还是不太明白第二点在表达什么...

    #include <bits/stdc++.h>
    #define LL long long
    #define debug
    using namespace std;
    //东方之珠 整夜未眠!
    const int N = 1e5+66;
    
    int n, m;
    pair<string, int>a[N];
    
    inline int calc(int bit, int now) {
       for (int i = 1; i <= n; ++ i) {
          int x = (a[i].second>>bit)&1;
          if (a[i].first == "AND") now &= x;
          else if (a[i].first == "OR") now |= x;
          else now ^= x;
       }
       return now;
    }
    
    inline int thestars() {
       cin >> n >> m;
       for (int i = 1; i <= n; ++ i) {
          char ch[5]; int x;
          scanf ("%s%d", ch, &x);
          a[i] = make_pair(ch, x);
       }
       int val = 0, ans = 0;
       for (int bit = 30; bit >= 0; -- bit) {
          int res0 = calc(bit, 0);
          int res1 = calc(bit, 1);
          if (val + (1<<bit) <= m && res0 < res1) 
             val += (1<<bit), ans += res1 << bit;
          else ans += res0 << bit;
       }
       cout << ans;
       return 0;
    }
    
    int youngore = thestars();
    
    signed main() {;}
    

    or xor

    click

    不会….

    晚上

    补坑 写博客 做题

  • 相关阅读:
    ArcGis Python脚本——遍历输出面或折线要素的折点坐标
    ArcGis Python脚本——根据接图表批量裁切分幅影像
    ArcGis安装失败提示“需要Microsoft .NET Framework 3.5 sp1或等效环境”的解决方法
    PLSQL Developer 远程连接Oracle数据库
    Oracle使用PLSQL导入数据后中文乱码的解决方法
    使用ArcMap做一个1:5000标准分幅图并编号
    ArcGis地理坐标系转投影坐标系问题的思考与处理
    CI框架 输入类
    PHP获取当前页面的URL作为参数以供下一层的页面可以返回上一层页面
    用js实现返回上一页
  • 原文地址:https://www.cnblogs.com/yszhyhm/p/13425599.html
Copyright © 2011-2022 走看看