要补的题:
牛客多校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
2∗n+1假设我们这条边的序号是
x
x
x,那么相邻边的序号
x
+
2
∗
n
+
1
x+2*n+1
x+2∗n+1,也就等价于
x
%
k
!
=
x
+
2
∗
n
+
1
%
k
x \%k != x+2*n+1\%k
x%k!=x+2∗n+1%k
也就是说我们要证明
g
c
d
(
k
,
2
∗
n
+
1
)
=
=
1
gcd(k,2*n+1)==1
gcd(k,2∗n+1)==1 已知
k
∣
2
∗
n
(
n
+
1
)
k|2*n(n+1)
k∣2∗n(n+1),
原命题等价于:
g
c
d
(
2
∗
n
+
1
,
2
∗
n
∗
(
n
+
1
)
)
gcd(2*n+1, 2*n*(n+1))
gcd(2∗n+1,2∗n∗(n+1))
g
c
d
(
2
∗
n
+
1
,
n
∗
(
2
∗
n
+
1
)
+
n
)
gcd(2*n+1, n*(2*n+1) +n)
gcd(2∗n+1,n∗(2∗n+1)+n)
g
c
d
(
2
∗
n
+
1
,
n
)
gcd(2*n+1, n)
gcd(2∗n+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=Tt∗k,若
t
∗
k
%
∣
T
∣
=
=
1
(|T|是环的大小,也就是循环节)
t*k\%|T|==1\text{(|T|是环的大小,也就是循环节)}
t∗k%∣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();
}