2020牛客暑假多校赛补题--第六场GHJ 第五场ACH 第二场 J

要补的题

牛客多校6:G H J
牛客多校5:A C H
牛客多校4:A C D H I
牛客多校3:A C D H I
牛客多校2:A E H I J K
牛客多校1:A B D H

(太菜了,只能补通过人数大于两位数的题。)

多校6 G:

大意是将n*n的网格图染色,然后要满足三个条件
1.每种颜色出现的数量相同
2.同一种颜色不能出现循环
3.平行线或者水平线都至少有两种颜色

解法:
构造题,先判断不可能的情况, n == 1 || k==1 || 2n(n+1)%k!=0
接下来给每条边编号,图转载自某大佬
盗的图
之后我们按照顺序给边上色。1->1, 2->2, k->k, k+1 ->1.
我们发现每一行中水平相邻的两条水平边和垂直边的颜色是不同的,因为序号是连续的。
接下来证明每一列中垂直相邻的边颜色也不一样:
我们发现垂直相邻的两条边序号是相差 2 ∗ n + 1 2*n+1 2n+1假设我们这条边的序号是 x x x,那么相邻边的序号 x + 2 ∗ n + 1 x+2*n+1 x+2n+1,也就等价于 x % k ! = x + 2 ∗ n + 1 % k x \%k != x+2*n+1\%k x%k!=x+2n+1%k
也就是说我们要证明 g c d ( k , 2 ∗ n + 1 ) = = 1 gcd(k,2*n+1)==1 gcd(k,2n+1)==1 已知 k ∣ 2 ∗ n ( n + 1 ) k|2*n(n+1) k2n(n+1),
原命题等价于:
g c d ( 2 ∗ n + 1 , 2 ∗ n ∗ ( n + 1 ) ) gcd(2*n+1, 2*n*(n+1)) gcd(2n+1,2n(n+1))
g c d ( 2 ∗ n + 1 , n ∗ ( 2 ∗ n + 1 ) + n ) gcd(2*n+1, n*(2*n+1) +n) gcd(2n+1,n(2n+1)+n)
g c d ( 2 ∗ n + 1 , n ) gcd(2*n+1, n) gcd(2n+1,n)
g c d ( n + 1 , n ) = = 1 gcd(n+1, n) == 1 gcd(n+1,n)==1

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
int main() {
  int t; read(t);
  while(t--) {
    int n, k; read(n); read(k);
    if(n == 1 || k == 1 || (n+1)*2*n%k != 0) {
      puts("-1");
      continue;
    }
    int st = 0, q = 2*n+1;
    for(int i = 1; i <= n + 1; i++) {
      for(int j = 1; j <= n; j++) {
        printf("%d%c", (st+j)%k+1, " \n"[j==n]);
      }
      st += q;
    }
    st = n+1;
    for(int i = 1; i <= n+1; i++) {
      for(int j = 1; j <= n; j++) {
        printf("%d%c", (st+(j-1)*q)%k+1, " \n"[j==n]);
      }
      st++;
    }
  }
}

多校6 H:

数位dp,双重循环枚举A和B的每一位即可

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
char s[105];
const int Mod = 1e9+7;
int len;
int dp[120][1802][2][2];
LL dfs(int pre, int x, bool li1, bool li2) {
  if(pre >= len) return x > 900;
  if(~dp[pre][x][li1][li2]) return dp[pre][x][li1][li2];
  int r = li1 ? s[pre]-'0' : 9;
  LL ret = 0;
  for(int i = 0; i <= r; i++) {
    for(int j = 0; j <= (li2?i:9); j++) {
      ret += dfs(pre+1, x+j-i,li1&(s[pre]=='0' + i), li2&(i==j));
      ret %= Mod;
    }
  }
  return dp[pre][x][li1][li2] = ret;
}
int main() {
  memset(dp, -1, sizeof dp);
  scanf("%s", s);
  len = strlen(s);
  cout << dfs(0, 900, 1, 1) << endl;  
}

多校2 J:

题目大意是要你找到一个全排列,使它置换k次后变成输入给你的全排列。
先找到置换群中的每个环,对于其中一个环 T T T,把它置换 k k k次后变成 S S S T k = = S T^k==S Tk==S,我们让 S S S置换t次,就等价于 S t = T t ∗ k S^t=T^{t*k} St=Ttk,若 t ∗ k % ∣ T ∣ = = 1 (|T|是环的大小,也就是循环节) t*k\%|T|==1\text{(|T|是环的大小,也就是循环节)} tk%T==1(|T|是环的大小,也就是循环节) S t = = T S^t==T St==T, 所以我们要求一个t,t是k在模|T|意义下的逆元,由于|T|不一定是质数,但是k是质数,所以可以直接用扩展欧几里得算法,当然也可以直接暴力,毕竟循环节最大是n。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e3+5;
int a[N], vis[N], b[N], p[N];
int main() {
  int n, k; read(n), read(k);
  _rep(1, n, i) read(a[i]);
  for(int i = 1, cnt, temp; i <= n; i++) {
    if(vis[i]) continue;
    cnt = 0, temp = i;
    while(a[temp] != i) {
      vis[a[temp]] = 1;
      b[cnt++] = temp;
      temp = a[temp];
    }
    b[cnt++] = temp;
    int inv;
    for(int j = 1; j < cnt; j++) if(j * 1ll* k % cnt == 1) {
      inv = j;
      break;
    }
    for(int j = 0; j < cnt; j++) {
      p[b[j]] = b[(j+inv)%cnt];
    }
  }
  _rep(1, n, i) printf("%d%c", p[i], " \n"[i==n]);
}

多校6:J

约瑟夫变换,约瑟夫环中的每次出队的序号组成了一个置换,多校2J可以帮我们0(n)实现多次变换。接下来是求这个置换,可以用些树形数据结构,找第k大。我用的是树状数组+二分。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e5+5;
int T[N], n, a[N], p[N], b[N], c[N];
bool vis[N];
void upd(int pos, int va) {for(; pos <= n; pos+=lowbit(pos)) T[pos] += va; }
int qry(int x) {int ret(0); for(;x;x-=lowbit(x))ret+=T[x]; return ret;}
void Move_x(int x) {
  for(int i = 1, cnt, temp; i <= n; i++) {
    if(vis[i]) continue;
    cnt = 0, temp = i;
    while(a[temp] != i) {
      vis[a[temp]] = 1;
      b[cnt++] = temp;
      temp = a[temp];
    }
    b[cnt++] = temp;
    for(int j = 0; j < cnt; j++) {
      c[b[j]] = p[b[(j+x)%cnt]];
    }
  }
  _rep(1, n, i) p[i] = c[i];
}
int main() {
  int m, k, x; read(n); read(m);
  _rep(1, n, i) p[i] = i;
  while(m--) {
    read(k); read(x);
    _rep(1, n, i) upd(i, 1), vis[i] = 0;
    int _l = 0, l, r;
    _rep(1, n, i) {
      int le = qry(n) - qry(_l), _kth = k, ans, _L;
      if(le >= k) l = _l + 1, r = n;
      else {
        _kth -= le;
        _l = 0;
        _kth %= (n-i+1);
        if(!_kth) _kth = n-i+1;
        l = _l+1, r = n;
      }
      _L=qry(_l);
      while(l <= r) {
        int mid = l + r >> 1;
        if(qry(mid)-_L >= _kth) {
          r = mid-1;
          ans = mid;
        } else l = mid+1;
      }
      a[i] = ans;
      _l = ans;
      upd(ans,-1);
    }
    Move_x(x);
  }
  _rep(1, n, i) printf("%d%c", p[i], " \n"[i==n]);
}

多校5 A

代码 :
题目大意是是有k个任务,你要按顺序完成它,你可以在你当前所在的点建立传送门,可以同时存在两个传送门,传送门间可以瞬间到达,你有一个遥控器,可以选择关闭任意的传送门。求完成k个任务要走的最短路。

这是一道dp题。首先想到最暴力的状态,dp[i][x][u][v] 代表第i个任务,当前在x,传送门在u,v。然后有一个显然的优化,我们可以把u这一维去掉。我们是要去v的。那么我们可以直接在x建一个传送门,没必要走到u再去v。

现在方程是dp[i][x][v],然后有一个很经典的优化,有k个任务,也就代表着我们要依次经过2*k个点,假设有一个任务u,v.那么我们的前进路线是1->u->v。当我们要转移到v时,这时所在的点肯定是u。因为我们没必要在中间停留,所以这样我们有压缩了一维,代价是第一维变成了两倍。

dp[i][v] 代表现在在第i-1个点,要去第i个,传送门在v。

我们可以枚举这次的传送门和上一次的传送门。发现要转移的情况不多。就可以写出来了。

郁闷啊,不想多写了,要是有人想看详细的再写

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
LL tu[301][301];
LL dp[2][301];
int a[602];
int main() { 
  int n, m, k, u, v, w, t = 1; read(n); read(m); read(k);
  _rep(1, n, i) _rep(1, n, j) if(i != j) tu[i][j] = 1e15;
  while(m--) { 
    scanf("%d %d %d", &u, &v, &w);
    tu[u][v] = tu[v][u] = min(tu[u][v], 1ll*w);
  } 
  a[1] = 1;
  while(k--) read(a[++t]), read(a[++t]);
  _rep(1, n, i) _rep(1, n, j) _rep(1, n, k)
    if(tu[j][k] > tu[j][i] + tu[i][k]) 
      tu[j][k] = tu[j][i] + tu[i][k];
  dp[1][1] = 0;
  _rep(2, n, i) dp[1][i] = 1e15;
  _rep(2, t, i) {
    int x = a[i-1], y = a[i];
    _rep(1, n, j) dp[i&1][j] = 1e15;
    _rep(1, n, j) {
      _rep(1, n, k) {
        if(k == j) dp[i&1][j] = min(dp[i&1][j], dp[i-1&1][j] + min(tu[x][y], tu[j][y]));
        dp[i&1][j] = min(dp[i&1][j], dp[i-1&1][k] + min(tu[x][j], tu[k][j]) + min(tu[j][y], tu[k][y]));
      }
    }
  }
  LL Min = 1e18;
  _rep(1, n, i) Min = min(Min, dp[t&1][i]);
  cout << Min << endl;
} 
 

多校5 C:

学习了一波母函数,学会了就能看懂题解了,推荐一个大佬的解题过程。
点击

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e6+5;
const LL Mod = 998244353;
LL f[N], inv[N];
LL ksm(LL a, LL b) {
  LL ret = 1;
  while(b) {
    if(b & 1) ret = ret * a % Mod;
    a = a * a % Mod;
    b >>= 1;
  }
  return ret;
}
void init() {
  f[0] = 1;
  _rep(1, N-1, i) f[i] = f[i-1] * i % Mod;
  inv[N-1] = ksm(f[N-1], Mod-2);
  for(int i = N-2; i >= 0; i--) {
    inv[i] = inv[i+1] * (i + 1) % Mod;
  }
}
LL C(int n, int m) {
  if(m > n) return 0;
  if(m == 0) return 1;
  if(n < N) return f[n] * inv[m] % Mod * inv[n-m] % Mod;
  LL ret = inv[m];
  _rep(n-m+1, n, i) ret = ret * i % Mod;
  return ret;
}
int main() {
  init();
  int t, n, m, k; read(t);
  while(t--) {
    read(n); read(m); read(k);
    if(n > m) swap(n, m);
    LL ans = 0;
    _rep(0, n-k, i) {
      ans = (ans + C(k+i-1, i) * C(n-i-1, n-k-i) % Mod * C(m-i-1, m-k-i)) % Mod;
    }
    cout << ans << endl;
  }
}

多校5 H

区间与的不同数字是很少的最多log(a[i])个,并且与之后一定不会增加,所以可以用线段树维护区间与,再用二分找出第一个出现的位置,然后再用主席树维护区间不同值。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e5+5;
int a[N];
int rt[N], cnt, n;
struct node {
  int l, r, sum;
}T[N*301];
void upd(int &now, int x, int va, int l = 1, int r = n) {
  T[++cnt] = T[now];
  now = cnt;
  T[now].sum += va;
  if(l == r) return ;
  int mid = l + r >> 1;
  if(x <= mid) upd(T[now].l, x, va, l, mid);
  else upd(T[now].r, x, va, mid+1, r);
}
int qry(int rt, int l, int r, int L, int  R) {
  if(l >= L && r <= R) return T[rt].sum;
  int mid = l + r >> 1, t = 0;
  if(mid >= L) t = qry(T[rt].l, l, mid, L, R);
  if(mid < R) t += qry(T[rt].r, mid+1, r, L, R);
  return t;
}
struct segtree{
  int T[N<<2];
  void build(int rt, int l, int r) {
    if(l == r) {
      T[rt] = a[l];
      return ;
    }
    int mid = l + r >> 1;
    build(lson);
    build(rson);
    T[rt] = T[rt<<1]&T[rt<<1|1];
  }
  int qry(int rt, int l, int r, int L, int R) {
    if(L <= l && R >= r) {
      return T[rt];
    }
    int mid = l + r >> 1, t = (1<<30)-1;
    if(mid >= L) t &= qry(lson, L, R);
    if(R > mid) t &= qry(rson, L, R);
    return t;
  }
}tr;
map<int, int> ma;
void init() {
  read(n);
  _rep(1, n, i) read(a[i]);
  tr.build(1, 1, n);
  for(int i = 1; i <= n; i++) {
    rt[i] = rt[i-1];
    if(ma.count(a[i])) upd(rt[i], ma[a[i]], -1);
    upd(rt[i], i, 1);
    ma[a[i]] = i;
    int cur = a[i];
    while(1) {
      int l = 1, r = i-1, mid, ret = 0;
      while(l <= r) {
        mid = l + r >> 1;
        if(tr.qry(1, 1, n, mid, i) < cur) ret = mid, l = mid + 1;
        else r = mid-1;
      } 
      if(!ret) break;
      cur = tr.qry(1,1,n,ret,i);
      if(ma.count(cur)) upd(rt[i], ma[cur], -1);
      ma[cur] = ret;
      upd(rt[i], ret, 1);
    } 
  }
}
void solve() {
  int q, l, r, ans = 0; read(q);
  while(q--) {
    read(l); read(r);
    l ^= ans; l = l % n + 1;
    r ^= ans; r = r % n + 1;
    if(l > r) swap(l, r);
    printf("%d\n", ans = qry(rt[r], 1, n, l, r));
  }
}
int main() {
  init();
  solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值