csp模拟10[欧几里得的噩梦, 清扫, 购物, ants]

T1 欧几里得的噩梦

嘴上说着要线性基,身体上还是很诚实得不要了嘛

  • 这个题其实仅仅是用了线性基的思想,和它没有半毛钱关系

  • 首先对于题目要求的限制大小最小以及字典序最小来分析

    • 大小最小

      • 说明了我们现在选的数已经很够了,再向里边加任何一个数都是多余的,即是对于现在我们想加入一个数,但是它如果能被我选完的数字异或出来,那他就可以跳过,没有加的必要,这样一通维护之后,我们就可以保证不冗余,也就是大小最小
    • 字典序最小

      • 其实根本不用考虑的说,因为我们是从前向后枚举的
        对于我已经选择了的数\(a, b, c\),我们考虑新来了一个数字\(k\),
        \(a \ xor \ b \ xor \ c = k\) ,而\(k\)的字典序比\(a\)小(假设\(a\)为其中字典序最大的),我们显然可以移项得\(k \ xor \ b \ xor \ c = a\),所以把\(k\)加进去,把\(a\)踢出来,这样字典序最小,同时大小不变
  • 考虑怎么判断一个数可以被异或出来

  • 因为每一个数的\(1\)最多有两位是\(1\),所以可以将这个判断抽象为判断图的联通,用并查集维护, 具体操作 :

    • 我们可以建立一个超级原点,将一个数字和它连边表示它存在于子集中,或者是可以通过子集中的数字得到($eg : $子集中有 \(x, x \ xor \ y\),那么就相当于是有了\(y\),处理\(x\)\(x \ xor \ y\)时会路径压缩以及连边,做到这个效果),当然,这个实际上是表示,我可以在子集中得到\(x\)位上是\(1\)的数
    • 对于有二进制位两个\(1\)的数字,我们可以判断这两个数字是否连边,如果未连边,就将两者连边,表示我加进去了这两个数异或的值

    这里注意并非是将两者和超级原点连边,因为和原点连边表示存在这两个数,而这里表示我有\(x \ xor y\),就是我有\(x \ xor y\)我不一定有\(x\),我也不一定有\(y\)

here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
  auto read = [](){
      LL x = 0;
      int f = 1;
      char c;
      while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
      do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
      return x * f;
  };
  template  <typename T> fuc(void, write)(T x){
      if (x < 0)putchar('-'), x = -x;
      if (x > 9)write(x / 10); putchar(x % 10 | '0');
  }
}

using namespace kiritokazuto;

const int maxn = 5e5 + 10,  Mod = 1e9 + 7;
const LL Inf = 2147483647;
int n, m;
int fa[maxn];
fuc(LL ,qpow)(LL x, LL y) {
  LL res = 1;
  while(y) {
      if(y & 1)res = (res * x) % Mod;
      x = (x * x) % Mod;
      y >>= 1;
  }
  return res % Mod;
}
int ans = 0;
int vis[maxn];
fuc(int, find)(int x){return (x == fa[x]) ? x : fa[x] = find(fa[x]);}
signed main(){
  n = read(), m = read();
  fr(i, 0, m)fa[i] = i;
  fr(i, 1, n) {
      int type = read();
      if(type == 1) {
          int x = read();
          if(find(x) == find(0))continue;
          else ans++, fa[find(x)] = find(0), vis[i] = 1;
      }  
      if(type == 2){
          int x = read(), y = read();
          if(find(x) == find(y)) {
              continue;
          }else 
          {
              ans++;
              fa[find(x)] = find(y);
              vis[i] = 1;
          }
      }
  
  }
  write((LL)qpow(2, ans) % Mod), fk;
  write(ans);
  ki;
  fr(i, 1, n){
      if(vis[i])printf("%d ", i);
  }
  return 0;
}

T2 清扫

  • 奶奶的,这个小题,巧滴hen(四声)

我们设当前子树的根节点为 \(rt\), 它有的石子数为 \(a[rt]\),子树总共需要抵消的石子数之和为 \(sum\) (也就是所有子树的 \(sz\) 和),子树中最多的一个的石子数为 \(Max\),子树内部相互抵消的次数为 \(x\) (即不同子树两两组队,抵消)。

  • 考虑两种抵消情况对 \(rt\) 的影响:
    • 子树内部相互抵消:
      • 每次 \(sum - 2\)\(a[rt] - 1\)
    • 子树内和外部抵消:
      • 每次 \(sum - 1\)\(a[rt] - 1\)
  • 首先如果我的 \(sum\) 直接就 比我的 \(a[rt]\) 大,显然不行。
  • 我们想让当前子树可以消完,那么我子树里还需要消除的次数必须和我还需的次数相等,如果多则会把我干成负的,少我就把他们干成负的,显然不行。
  • 所以我们可以解一个方程 \(sum - 2 \times x = a[rt] - x\),得出:

\[sum - a[rt] = x \]

  • 所以我(既是我的,也是我子树的)剩下的所需要的次数为:

\[a[rt] - x = 2 \times a[rt] - sum \]

  • 当然,这里我们还需要考虑一种非法情况: 如果我有一颗特别大的子树,那么我其他的子树每一个人都和他消一次是最优的,所以除去自己的石子,它还应该被消 \(sum - Max\) 次, 所以它最终剩下:

\[Max - (sum - Max) = 2 \times Max - sum \]

  • 个石子(当然,这个的值有可能是负的)。那么我们需要判断一下,我根节点现在还需要消除的次数应该是要比它多的,否则是不行的,会有负数,所以这个题就干完了。
  • 当然,对于两个点的情况可以特判一下,我的根应该选一个度数大于 \(1\) 的节点,以及最终我的 \(sz[rt] = 0\) 是必须的。
  • 之后就 \(dfs\) 一遍统计答案就行。
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
    auto read = [](){
        LL x = 0;
        int f = 1;
        char c;
        while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
        do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
        return x * f;
    };
    template  <typename T> fuc(void, write)(T x){
        if (x < 0)putchar('-'), x = -x;
        if (x > 9)write(x / 10); putchar(x % 10 | '0');
    }
}

using namespace kiritokazuto;

const int maxn = 2e5 + 100, Mod = 1e9 + 7;
const LL Inf = 2147483647;



int n;
vector <int> wmx[maxn];
int sz[maxn], son[maxn];
int a[maxn];
int rt = 1;
fuc(bool, dfs)(int x, int pre) {
    if(wmx[x].size() == 1)return sz[x] = a[x], 1;
    for(auto to : wmx[x]) {
        if(to != pre) {
            if(!dfs(to, x))re(0);
            sz[x] += sz[to];
            if(!son[x] || sz[son[x]] < sz[to])son[x] = to;
        }
    }
    if(sz[x] < a[x])re(0);
    int tmp = (sz[son[x]] * 2) - sz[x];
    sz[x] = (a[x] * 2) - sz[x];
    return sz[x] >= max(tmp, 0);

} 
signed main() {  
    n =read();
    fr(i, 1, n)a[i] = read();
    delfr(i, 1, n) {
        int x = read(), y = read();
        wmx[x].pb(y);
        wmx[y].pb(x);
    }
    while(wmx[rt].size() == 1)rt++;
    if(rt == n + 1) {
        (a[1] == a[2]) ? puts("YES") : puts("NO");
        re(0);
    }
    (dfs(rt, 0) && sz[rt] == 0 ? puts("YES") : puts("NO"));
} 

T3 购物

应该是很多人的场切题目了,这玩意照旁边 \(whpan\) 说大力分类讨论就行,em

  • 其实我不想写了..
  • 对于一个数 \(x\), 那么它的贡献区间就是 \([\left \lceil \frac{x}{2} \right \rceil,x]\), 我们可以考虑每一个数的贡献都贡献在了哪里:
    • 首先维护一个前缀和\(sum\), 它就是我前\(i - 1\)个数贡献的上界(显然),
      然后分类讨论
  • 由于我懒得写,所以看 \(whp\)的吧
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first

using namespace std;
namespace kiritokazuto{
    auto read = [](){
        LL x = 0;
        int f = 1;
        char c;
        while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
        do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
        return x * f;
    };
    template <typename T> fuc(void, write)(T x){
        if (x < 0)putchar('-'), x = -x;
        if (x > 9)write(x / 10); putchar(x % 10 | '0');
    }
}

using namespace kiritokazuto;

const int maxn = 3e5 + 100, Mod = 1e9 + 7;
const LL Inf = 1e18, ord = 1e10;

int n;
unordered_map <LL, int> Map;
int a[maxn];
LL up = 0;
fuc(void, work1)();
fuc(void, work2)();
fuc(void, work3)();
LL down = 0;
LL sum = 0;
LL ans = 0;
signed main() { 
    n = read();
    fr(i, 1, n) a[i] = read(), sum += a[i];
    // if(n == 1) {
    //     work1();
    //     re(0);
    // }
    // if(n == 2) {
    //     work2();
    //     re(0);
    // }
    // if(n == 3) { 
    //     work3();
    //     re(0)  ;
    // }
    sort(a + 1, a + n + 1);
    fr(i, 1, n) {
        down = (LL)ceil(1.0 * a[i] / 2);//下界
        if(down > up)ans = ans + up + a[i] - down + 1;//大于上界
        else ans += a[i];
        up += a[i];
    }
    write(ans);
    re(0);
}  
fuc(void, work1)() {
    LL tmp = (LL)ceil(1.0 * sum / 2);
    write(sum - tmp + 1);
    return;
}
fuc(void, work2)() {
    LL tmp1 = (LL)ceil(1.0 * a[1] / 2);
    LL tmp2 = (LL)ceil(1.0 * a[2] / 2);
    LL tmp3 = (LL)ceil(1.0 * sum / 2);
    LL ans = 0 ;
    fr(i, tmp1, a[1]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp2, a[2]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp3, sum) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    write(ans);
    return;
}
fuc(void, work3)() {
    LL tmp1 = (LL)ceil(1.0 * a[1] / 2);
    LL tmp2 = (LL)ceil(1.0 * a[2] / 2);
    LL tmp3 = (LL)ceil(1.0 * a[3] / 2);
    LL tmp4 = (LL)ceil(1.0 * (a[1] + a[2]) / 2);
    LL tmp5 = (LL)ceil(1.0 * (a[2] + a[3]) / 2);
    LL tmp6 = (LL)ceil(1.0 * (a[1] + a[3]) / 2);
    LL tmp7 = (LL)ceil(1.0 * sum / 2);
    LL ans = 0 ;
    fr(i, tmp1, a[1]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp2, a[2]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp3, a[3]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp4, a[1] + a[2]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp5, a[2] + a[3]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp6, a[1] + a[3]) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    fr(i, tmp7, sum) {
        if(Map.find(i) == Map.end()) {
            Map[i]++;
            ans++;
        }
    }
    write(ans);
    return;
}

T4 ants

  • permu升级版板子,也就是回滚莫队板子
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMXX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
    auto read = [](){
        LL x = 0;
        int f = 1;
        char c;
        while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
        do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
        return x * f;
    };
    template  <typename T> fuc(void, write)(T x){
        if (x < 0)putchar('-'), x = -x;
        if (x > 9)write(x / 10); putchar(x % 10 | '0');
    }
}

using namespace kiritokazuto;

const int maxn = 1e5 + 10,  Mod = 1e9 + 7;
const LL Inf = 2147483647;

int n, m;
int bel[maxn];
int a[maxn];
int ans[maxn];
int st[maxn], ed[maxn];
int sq;
struct Node {
    int l, r, id;
    friend bool operator < (Node A, Node B) {
        return (bel[A.l] == bel[B.l]) ? A.r < B.r : bel[A.l] < bel[B.l];
    }
}q[maxn];
struct WMX {
    int type, pos, val;
    WMX(){};
    WMX(int x, int y, int z){type = x, pos = y, val = z;};
}qq[maxn * 2];
int lid[maxn], rid[maxn];
int top;
signed main(){
   n = read();
   m = read();
   sq = sqrt(n);
   fr(i, 1, n) {
       a[i] = read();
    //    bel[i] = (i - 1) / sq + 1;
   }
   fr(i, 1, m) {
       q[i].l = read();
       q[i].r = read();
       q[i].id = i;
   }
   fr(i, 1, sq) {
       st[i] = (n / sq) * (i - 1) + 1;
       ed[i] = (n / sq) * i;
       if(i == sq)ed[i] = n;
       fr(j, st[i], ed[i])bel[j] = i;
   }
   sort(q + 1, q + m + 1);
   int r = 0, sum = 0;
   fr(i, 1, m) {
        if(bel[q[i].l] != bel[q[i - 1].l]) {
            sum = 0;
            fr(i, 1, n)lid[a[i]] = rid[a[i]] = 0;
            r = ed[bel[q[i].l]];
        }
        while(r < q[i].r) {
            r ++;
            lid[a[r]] = lid[a[r] - 1] + 1;
            rid[a[r]] = rid[a[r] + 1] + 1;
            int tmp = lid[a[r]] + rid[a[r]] - 1;
            sum = max(sum, tmp);
            lid[a[r] + rid[a[r]] - 1] = rid[a[r] - lid[a[r]] + 1] = tmp;
        }
        int res = sum;
        top = 0;
        int now = min(q[i].r, ed[bel[q[i].l]]);
        fr(j, q[i].l, now)  {
            lid[a[j]] = lid[a[j] - 1] + 1;
            rid[a[j]] = rid[a[j] + 1] + 1;
            int tmp = lid[a[j]] + rid[a[j]] - 1;
            res = max(res, tmp);
            qq[++top]=WMX(1, a[j] + rid[a[j]] - 1,lid[a[j] + rid[a[j]] - 1]);
            qq[++top]=WMX(2, a[j] - lid[a[j]] + 1, rid[a[j] - lid[a[j]] + 1]);
            lid[a[j] + rid[a[j]] - 1] = rid[a[j] - lid[a[j]] + 1] = tmp;
        }
       fp(i, top, 1) {
           if(qq[i].type == 1)lid[qq[i].pos] = qq[i].val;
           else rid[qq[i].pos] = qq[i].val;
       }
       fr(j, q[i].l, now)lid[a[j]] = rid[a[j]] = 0;
       ans[q[i].id] = res;

   }
   fr(i, 1, m)write(ans[i]), ki;
   re(0);
} 

        


image

posted @ 2022-09-28 10:15  kiritokazuto  阅读(35)  评论(7编辑  收藏  举报