2021“MINIEYE杯”中国大学生算法设计超级联赛(1)【解题报告】

本文主要是介绍2021“MINIEYE杯”中国大学生算法设计超级联赛(1)【解题报告】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Replay

Wq0Con.gif

Problem A. Mod, Or and Everything

题目大意

T T T 组输入,每组一个整数 n n n,求 ( n m o d 1 ) O R ( n m o d 2 ) O R ⋯ O R ( n m o d n ) (n \; mod \; 1) \; OR \; (n \; mod \; 2) \; OR \; \cdots \; OR \; (n \; mod \; n) (nmod1)OR(nmod2)OROR(nmodn).

1 ≤ T ≤ 5000 , 1 ≤ n ≤ 1 0 12 1 \leq T \leq 5000, 1 \leq n \leq 10^{12} 1T5000,1n1012.

分析

分析个啥,直接打表找规律.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e5;
const int N = (int)1e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;void work()
{
//    for(int i = 1; i <= 100; ++i)
//    {
//        int s = 0;
//        for(int j = 1; j <= i; ++j) s |= i % j;
//        printf("%d: %d\n", i, s);
//    }ll n; scanf("%lld", &n);if(n == 1){printf("0\n");return;}for(ll i = 0; ; ++i){if(((1ll<<i) < n) && n <= ((1ll<<i+1))){printf("%lld\n", (1ll<<i) - 1);break;}}
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();return 0;
}

Problem B. Rocket land

Problem C. Puzzle loop

题目大意

T T T 组输入,每组输入一个大小为 ( n − 1 ) × ( m − 1 ) (n - 1) \times (m - 1) (n1)×(m1) 的地图,地图字符集为 { ′ 0 ′ , ′ 1 ′ , ′ . ′ } \{'0', '1', '.'\} {0,1,.}.

′ 0 ′ '0' 0 表示该格子周围四条边有偶数条红边.

′ 1 ′ '1' 1 表示该格子周围四条边有奇数条红边.

′ . ′ '.' . 表示该格子周围四条边无限制.

要求红边形成若干个边不相交的环,求方案数.

1 ≤ T ≤ 100 , 1 ≤ n , m ≤ 17 1 \leq T \leq 100, 1 \leq n, m \leq 17 1T100,1n,m17.

分析

红边实质上就是若个干不相交的欧拉路嘛,因此要求每个交点的度为偶数,这样便满足了形成若干个边不相交的环这一条件.

此外对每个格子的四条边建异或方程.

于是得到异或方程组,高斯消元即可.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e6;
const int N = (int)17;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;int n, m;
char s[N + 5][N + 5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int row, col;
bool a[2 * (N + 1) * (N + 1) + 5][2 * N * (N + 1) + 5];inline int id(int x1, int y1, int x2, int y2)
{if(x1 > x2) swap(x1, x2);if(y1 > y2) swap(y1, y2);if(x1 == x2) return (x1 - 1) * m + y1;else         return (n + 1) * m + (x1 - 1) * (m + 1) + y1;
}void build()
{row = 0, col = (n + 1) * m + n * (m + 1);for(int i = 1; i <= n + 1; ++i){for(int j = 1; j <= m + 1; ++j){++row;for(int k = 1; k <= col + 1; ++k) a[row][k] = 0;for(int k = 0, x, y; k < 4; ++k){x = i + dx[k], y = j + dy[k];if(x < 1 || x > n + 1 || y < 1 || y > m + 1)    continue;a[row][id(x, y, i, j)] = 1;}}}for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){if(s[i][j] == '.') continue;++row;for(int k = 1; k <= col + 1; ++k) a[row][k] = 0;a[row][id(i, j, i, j + 1)] = 1;a[row][id(i, j, i + 1, j)] = 1;a[row][id(i, j + 1, i + 1, j + 1)] = 1;a[row][id(i + 1, j, i + 1, j + 1)] = 1;a[row][col + 1] = s[i][j] - '0';}}
//    for(int i = 1; i <= row; ++i)
//    {
//        bool f = 0;
//        for(int j = 1; j <= col; ++j)
//        {
//            if(!a[i][j])    continue;
//            if(f) printf(" ^ ");
//            printf("x(%d)", j);
//            f = 1;
//        }
//        printf(" = 0\n");
//    }
}int gauss(int n, int m)
{int c, r;for(c = 1, r = 1; c <= m; ++c){int t = r;for(int i = r; i <= n; ++i){if(a[i][c])   t = i;}if(!a[t][c]) continue;for(int i = c; i <= m + 1; ++i) swap(a[t][i], a[r][i]);for(int i = 1; i <= n; ++i){if(i == r)  continue;if(a[i][c]){for(int j = m + 1; j >= c; --j){a[i][j] ^= a[r][j];}}}++r;}for(int i = r; i <= n; ++i){if(a[i][m + 1])   return -1;//无解}return m - r + 1;//自由元个数
}ll quick(ll a, ll b, ll p = mod)
{ll s = 1;while(b){if(b & 1) s = s * a % p;a = a * a % p;b >>= 1;}return s;
}void work()
{scanf("%d %d", &n, &m);--n, --m;for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);build();int r = gauss(row, col);if(r == -1) printf("0\n");else        printf("%lld\n", quick(2, r));
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

bitset 优化

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e6;
const int N = (int)17;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;int n, m;
char s[N + 5][N + 5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int row, col;
bitset<614> a[2 * (N + 1) * (N + 1) + 5];inline int id(int x1, int y1, int x2, int y2)
{if(x1 > x2) swap(x1, x2);if(y1 > y2) swap(y1, y2);if(x1 == x2) return (x1 - 1) * m + y1;else         return (n + 1) * m + (x1 - 1) * (m + 1) + y1;
}void build()
{row = 0, col = (n + 1) * m + n * (m + 1);for(int i = 1; i <= n + 1; ++i){for(int j = 1; j <= m + 1; ++j){++row;a[row].reset();for(int k = 0, x, y; k < 4; ++k){x = i + dx[k], y = j + dy[k];if(x < 1 || x > n + 1 || y < 1 || y > m + 1)    continue;a[row].set(id(x, y, i, j));}}}for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j){if(s[i][j] == '.') continue;++row;a[row].reset();a[row].set(id(i, j, i, j + 1));a[row].set(id(i, j, i + 1, j));a[row].set(id(i, j + 1, i + 1, j + 1));a[row].set(id(i + 1, j, i + 1, j + 1));a[row].set(col + 1, s[i][j] - '0');}}
//    for(int i = 1; i <= row; ++i)
//    {
//        bool f = 0;
//        for(int j = 1; j <= col; ++j)
//        {
//            if(!a[i][j])    continue;
//            if(f) printf(" ^ ");
//            printf("x(%d)", j);
//            f = 1;
//        }
//        printf(" = 0\n");
//    }
}int gauss(int n, int m)
{int c, r;for(c = 1, r = 1; c <= m; ++c){int t = r;for(int i = r; i <= n; ++i){if(a[i][c])   t = i;}if(!a[t][c]) continue;swap(a[t], a[r]);for(int i = 1; i <= n; ++i){if(i == r)  continue;if(a[i][c]) a[i] ^= a[r];}++r;}for(int i = r; i <= n; ++i){if(a[i][m + 1])   return -1;//无解}return m - r + 1;//自由元个数
}ll quick(ll a, ll b, ll p = mod)
{ll s = 1;while(b){if(b & 1) s = s * a % p;a = a * a % p;b >>= 1;}return s;
}void work()
{scanf("%d %d", &n, &m);--n, --m;for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);build();int r = gauss(row, col);if(r == -1) printf("0\n");else        printf("%lld\n", quick(2, r));
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

Problem D. Another thief in a Shop

题目大意

T T T 组输入,每组给 n n n k k k.

n n n 个物品,价值分别为 a [ 1 ] , a [ 2 ] , ⋯ , a [ n ] a[1], a[2], \cdots, a[n] a[1],a[2],,a[n],每件物品有无穷多件.

求拼凑出总价值为 k k k 的方案数.

1 ≤ T ≤ 100 , 1 ≤ n ≤ 100 , 1 ≤ k ≤ 1 0 18 , 1 ≤ a [ i ] ≤ 10 1 \leq T \leq 100, 1 \leq n \leq 100, 1 \leq k \leq 10^{18}, 1 \leq a[i] \leq 10 1T100,1n100,1k1018,1a[i]10.

分析

f [ i ] [ j ] f[i][j] f[i][j] 表示考虑了前 i i i 个物品,总价值为 j j j 的方案数.

则有状态转移方程 f [ i ] [ j ] = ∑ l = 0 ⌊ j a [ i ] ⌋ f [ i − 1 ] [ j − l × a [ i ] ] \begin{aligned} f[i][j] = \sum_{l = 0}^{\lfloor \frac{j}{a[i]} \rfloor}{f[i - 1][j - l \times a[i]]} \end{aligned} f[i][j]=l=0a[i]jf[i1][jl×a[i]].

接下来这一步被题解惊艳到了,拉格朗日插值!!!

但体会一下也就明白了,还是很妙的感觉.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e2;
const int N = (int)5e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;int n; ll k;
int a[M + 5];
int f[N + 5];
int y[M + 5];
int pre[M + 5];
int suf[M + 5];
int fac[M + 5];
int inv[M + 5];
int invfac[M + 5];void init()
{fac[0] = fac[1] = inv[1] = invfac[0] = invfac[1] = 1;for(int i = 2; i <= M; ++i){fac[i] = 1ll * fac[i - 1] * i % mod;inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;invfac[i] = 1ll * invfac[i - 1] * inv[i] % mod;}
}int gcd(int a, int b)
{return b ? gcd(b, a % b) : a;
}int Lagrange(int n, ll k)
{if(k <= n + 1) return y[k];pre[0] = 1;     for(int i = 1; i <= n + 1; ++i) pre[i] = (k - i) % mod * pre[i - 1] % mod;suf[n + 2] = 1; for(int i = n + 1; i >= 1; --i) suf[i] = (k - i) % mod * suf[i + 1] % mod;int ans = 0, cur;for(int i = 1; i <= n + 1; ++i){cur = 1ll * pre[i - 1] * suf[i + 1] % mod * invfac[i - 1] % mod * invfac[n + 1 - i] % mod;if(n + 1 - i & 1) cur *= -1;cur = 1ll * cur * y[i] % mod;ans = (ans + cur) % mod;}ans = (ans % mod + mod) % mod;return ans;
}void work()
{scanf("%d %lld", &n, &k);int lcm = 1;for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), lcm = lcm / gcd(lcm, a[i]) * a[i];memset(f, 0, sizeof(f)); f[0] = 1;int r = lcm * n;for(int i = 1; i <= n; ++i){for(int j = a[i]; j <= r; ++j) f[j] = (f[j] + f[j - a[i]]) % mod;}for(int i = k % lcm; i < r; i += lcm) y[i / lcm + 1] = f[i];printf("%d\n", Lagrange(n - 1, k / lcm + 1));
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);init();int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

Problem E. Minimum spanning tree

题目大意

T T T 组输入,每组给出一个 n n n.

点的编号为 1 , 2 , ⋯ , n 1, 2, \cdots, n 1,2,,n,点 a a a 与 点 b b b 的权值定义为 l c m ( a , b ) lcm(a, b) lcm(a,b).

求这个图的最小生成树.

1 ≤ T ≤ 1 0 2 , 2 ≤ n ≤ 1 0 7 1 \leq T \leq 10^2, 2 \leq n \leq 10^7 1T102,2n107.

分析

显然合数与它的因子连边,质数与 2 2 2 连边最优.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e7;
const int N = (int)1e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;int cnt, prime[M + 5];
bool is_prime[M + 5];
ll f[M + 5];void init()
{memset(is_prime, 1, sizeof(is_prime));cnt = is_prime[0] = is_prime[1] = 0;for(int i = 2; i <= M; ++i){if(is_prime[i]) prime[++cnt] = i;for(int j = 1; j <= cnt && i * prime[j] <= M; ++j){is_prime[i * prime[j]] = 0;if(i % prime[j] == 0) break;}}f[2] = 0;for(int i = 3; i <= M; ++i){if(is_prime[i]) f[i] = f[i - 1] + 2 * i;else            f[i] = f[i - 1] + i;}
}void work()
{int n; scanf("%d", &n);printf("%lld\n", f[n]);
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);init();int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();return 0;
}

Problem F. Xor sum

题目大意

T T T 组输入,每组输入有两个整数 n n n k k k,接下来 n n n 个整数表示 a [ 1 ] , a [ 2 ] , ⋯ , a [ n ] a[1], a[2], \cdots, a[n] a[1],a[2],,a[n].

要求找一段区间,使得这段区间的异或和不小于 k k k,若不存在则输出 − 1 -1 1,存在则找到满足上述条件最短的区间,若仍不唯一,则输出最靠左的区间.

1 ≤ T ≤ 1 0 2 , 1 ≤ n ≤ 1 0 5 , 0 ≤ k , a [ i ] < 2 30 1 \leq T \leq 10^2, 1 \leq n \leq10^5, 0 \leq k, a[i] < 2^{30} 1T102,1n105,0k,a[i]<230.

分析

s [ i ] = a [ 1 ] X O R a [ 2 ] X O R ⋯ X O R a [ i ] s[i] = a[1] \; XOR \; a[2] \; XOR \; \cdots \; XOR \;a[i] s[i]=a[1]XORa[2]XORXORa[i].

则区间 [ l , r ] [l, r] [l,r] 异或和不小于 k k k 等价于 s [ r ] X O R s [ l − 1 ] s[r] \; XOR \; s[l-1] s[r]XORs[l1] 不小于 k k k.

因此字典树维护 s [ i ] s[i] s[i],节点记录当前最新的时间戳,对于两边都能走的情况就选择最新的时间戳走.

详见代码.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e5;
const int N = (int)29;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;struct tnode
{int nx[2], stamp;
} tree[M * 31 + 5];
int cnt;int n, k;void insert(int p, int d, int x, int stamp)
{if(d == -1) return;int v = (x>>d&1);if(!tree[p].nx[v]){tree[p].nx[v] = ++cnt;tree[tree[p].nx[v]].nx[0] = 0;tree[tree[p].nx[v]].nx[1] = 0;tree[tree[p].nx[v]].stamp = 0;}tree[p].stamp = stamp;insert(tree[p].nx[v], d - 1, x, stamp);if(d == 0){tree[tree[p].nx[v]].stamp = stamp;}
}inline int setZero(int x, int k)
{return x&(~(1<<k));
}inline int setOne(int x, int k)
{return x|(1<<k);
}int queryMx(int x, int p, int d)
{for(int i = d, v; i >= 0; --i){v = (x>>i&1);if(tree[p].nx[!v]) p = tree[p].nx[!v], x = setOne(x, i);else               p = tree[p].nx[v], x = setZero(x, i);}return x;
}int mi, l, r;void query(int x, int stamp, bool f = 0)
{int p = 0;for(int i = N, vk, vx; i >= 0; --i){vk = (k>>i&1);vx = (x>>i&1);bool canZero = 0, canOne = 0;if(tree[p].nx[vx])  canZero = queryMx(setZero(x, i), tree[p].nx[vx], i - 1) >= k;if(tree[p].nx[!vx]) canOne = queryMx(setOne(x, i), tree[p].nx[!vx], i - 1) >= k;if(canZero && canOne){if(tree[tree[p].nx[vx]].stamp > tree[tree[p].nx[!vx]].stamp) p = tree[p].nx[vx], x = setZero(x, i);else                                                         p = tree[p].nx[!vx], x = setOne(x, i);}else if(canZero)    p = tree[p].nx[vx], x = setZero(x, i);else if(canOne)     p = tree[p].nx[!vx], x = setOne(x, i);else                return;}if(mi > stamp - tree[p].stamp){mi = stamp - tree[p].stamp;l = tree[p].stamp + 1, r = stamp;}
}void work()
{mi = inf, l = -1, r = -1;cnt = tree[0].nx[0] = tree[0].nx[1] = tree[0].stamp = 0;scanf("%d %d", &n, &k);insert(0, N, 0, 0);int s = 0;for(int i = 1, a; i <= n; ++i){scanf("%d", &a);s ^= a;query(s, i);insert(0, N, s, i);}if(mi == inf) printf("-1\n");else          printf("%d %d\n", l, r);
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();return 0;
}

Problem G. Pass!

题目大意

T T T 组输入,每组有两个整数 n n n x x x.

一共有 n n n 个人和 1 1 1 个球,初始球在第 1 1 1 人脚下,每秒拥有球的那个人会把球传给另一个人.

经过 t t t 秒后,球又回到了第 1 1 1 个人脚下,方案数为 x x x.

现在给出 x x x 要求反解最小非负整数 t t t.

分析

f [ i ] [ 0 ] f[i][0] f[i][0] 表示第 i i i 秒末球不在第 1 1 1 个人脚下的方案数,
f [ i ] [ 1 ] \quad f[i][1] f[i][1] 表示第 i i i 秒末球在第 1 1 1 个人脚下的方案数.

则有如下转移方程:

{ f [ 0 ] [ 1 ] = 1 f [ i ] [ 0 ] = ( n − 2 ) f [ i − 1 ] [ 0 ] + ( n − 1 ) f [ i − 1 ] [ 1 ] f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] → { f [ 1 ] [ 0 ] = n − 1 f [ 2 ] [ 0 ] = ( n − 1 ) ( n − 2 ) f [ i ] [ 0 ] = ( n − 2 ) f [ i − 1 ] [ 0 ] + ( n − 1 ) f [ i − 2 ] [ 0 ] i ≥ 3 \left\{ \begin{aligned} f[0][1] &= 1 \\ f[i][0] &= (n - 2)f[i - 1][0] + (n - 1)f[i - 1][1] \\ f[i][1] &= f[i - 1][0] \end{aligned} \right. \rightarrow \left\{ \begin{aligned} f[1][0] &= n - 1 \\ f[2][0] &= (n - 1)(n - 2) \\ f[i][0] &= (n - 2)f[i - 1][0] + (n - 1)f[i - 2][0] \quad i \geq 3\\ \end{aligned} \right. f[0][1]f[i][0]f[i][1]=1=(n2)f[i1][0]+(n1)f[i1][1]=f[i1][0]f[1][0]f[2][0]f[i][0]=n1=(n1)(n2)=(n2)f[i1][0]+(n1)f[i2][0]i3

学了一下二阶线性递推式的通项求法,夙愿清单减减.

于是可以得到 f [ i ] [ 0 ] f[i][0] f[i][0] 的解析式为 f [ i ] [ 0 ] = ( n − 1 ) 2 n ( n − 1 ) i − 1 + n − 1 n ( − 1 ) i − 1 f[i][0] = \frac{(n -1)^2}{n}(n-1)^{i - 1} + \frac{n - 1}{n}(-1)^{i - 1} f[i][0]=n(n1)2(n1)i1+nn1(1)i1.

又由 f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] f[i][1] = f[i - 1][0] f[i][1]=f[i1][0]

f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] = ( n − 1 ) 2 n ( n − 1 ) i − 2 + n − 1 n ( − 1 ) i − 2 i ≥ 0 = { ( n − 1 ) i n − n − 1 n i 为 奇 数 ( n − 1 ) i n + n − 1 n i 为 偶 数 = { ( n − 1 ) 2 k + 1 n − n − 1 n i = 2 k + 1 , k ≥ 0 ( n − 1 ) 2 k n + n − 1 n i = 2 k , k ≥ 0 ⟶ 即 解 { ( n − 1 ) 2 k ≡ n x + n − 1 n − 1 ( m o d p ) ( n − 1 ) 2 k ≡ n x − n + 1 ( m o d p ) \begin{aligned} f[i][1] &= f[i - 1][0] \\ &= \frac{(n -1)^2}{n}(n-1)^{i - 2} + \frac{n - 1}{n}(-1)^{i - 2} \quad i \geq 0 \\ &= \left\{ \begin{aligned} \frac{(n -1)^i}{n} &- \frac{n - 1}{n} \quad i 为奇数 \\ \frac{(n -1)^i}{n} &+ \frac{n - 1}{n} \quad i 为偶数 \\ \end{aligned} \right. \\ &= \left\{ \begin{aligned} \frac{(n -1)^{2k+1}}{n} &- \frac{n - 1}{n} \quad i = 2k+1, k \geq 0\\ \frac{(n -1)^{2k}}{n} &+ \frac{n - 1}{n} \quad i = 2k, k \geq 0\\ \end{aligned} \right. \\ &\stackrel{即解}{\longrightarrow} \left\{ \begin{aligned} (n-1)^{2k} &\equiv \frac{nx + n - 1}{n - 1} \; (mod\; p) \\ (n-1)^{2k} &\equiv nx - n + 1\; (mod \; p) \\ \end{aligned} \right. \\ \end{aligned} f[i][1]=f[i1][0]=n(n1)2(n1)i2+nn1(1)i2i0=n(n1)in(n1)inn1i+nn1i=n(n1)2k+1n(n1)2knn1i=2k+1,k0+nn1i=2k,k0(n1)2k(n1)2kn1nx+n1(modp)nxn+1(modp)

至此套上 BSGS 即可.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e5;
const int N = (int)1e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;struct hashTable
{const static int M = (int)1e5;const static int N = (int)31601;int head[N + 5], cnt;struct node{int v, w, nx;} a[M + 5];void init(){cnt = 0;memset(head, -1, sizeof(head));}void add(int v, int w){for(int i = head[w % N]; ~i; i = a[i].nx){if(a[i].w == w){a[i].v = v;return;}}a[cnt].v = v;a[cnt].w = w;a[cnt].nx = head[w % N];head[w % N] = cnt++;}int tofind(int w){for(int i = head[w % N]; ~i; i = a[i].nx){if(a[i].w == w) return a[i].v;}return -1;}
} H;struct LOG
{ll quick(ll a, ll b, ll p = mod){ll s = 1;while(b){if(b & 1) s = s * a % p;a = a * a % p;b >>= 1;}return s;}ll inv(ll n, ll p = mod){return quick(n, p - 2, p);}int k = 31596, t;void init(int a, int p){H.init();for(int i = 0, j = 1; i < k; ++i){H.add(i, j);j = 1ll * j * a % p;}t = quick(a, k, p);}int BSGS(int a, int b, int p){a = (1ll * a % p + p) % p, b = (1ll * b % p + p) % p;if(b == 1)  return 0;for(int i = 1, j = t * inv(b, p) % p, h; i <= k; ++i){h = H.tofind(j);if(~h && 1ll * k * i > h && !((1ll * k * i - h)&1)) return 1ll * k * i - h;j = 1ll * j * t % p;}return -1;}
} B;int n, x;int calOdd()
{int a = (1ll * n * x % mod + n - 1 + mod) % mod * B.inv(n - 1) % mod;int x = B.BSGS(n - 1, a, mod);if(x == -1) return inf;return x + 1;
}int calEven()
{int a = (1ll * n * x % mod - n + 1 + mod) % mod;int x = B.BSGS(n - 1, a, mod);if(x == -1) return inf;return x;
}void work()
{scanf("%d %d", &n, &x);B.init(n - 1, mod);int odd = calOdd(), even = calEven();int mi = min(odd ,even);if(mi == inf)   printf("-1\n");else            printf("%d\n", mi);
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

Problem H. Maximal submatrix

题目大意

T T T 组输入,每组输入一个 n × m n \times m n×m 的矩阵.

求一个最大子矩阵,这个子矩阵每一列中的元素递增,输出最大子矩阵的大小.

1 ≤ T ≤ 10 , 1 ≤ n , m ≤ 2000 1 \leq T \leq 10, 1 \leq n, m \leq 2000 1T10,1n,m2000.

分析

单调栈经典题了.

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)2e3;
const int N = (int)1e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;int n, m;
int a[M + 5][M + 5];
int b[M + 5][M + 5];int s[M + 5], w[M + 5], p;int cal(int a[])
{a[m + 1] = p = 0;int ans = 0;for(int i = 1; i <= m + 1; ++i){if(a[i] > s[p]){s[++p] = a[i], w[p] = 1;}else{int width = 0;while(s[p] > a[i]){width += w[p];ans = max(ans, width * s[p]);p--;}s[++p] = a[i], w[p] = width + 1;}}return ans;
}void work()
{scanf("%d %d", &n, &m);for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) scanf("%d", &a[i][j]);for(int j = 1; j <= m; ++j){b[1][j] = 1;for(int i = 2; i <= n; ++i){if(a[i][j] >= a[i - 1][j]) b[i][j] = b[i - 1][j] + 1;else                       b[i][j] = 1;}}int mx = 0;for(int i = 1; i <= n; ++i){mx = max(mx, cal(b[i]));}printf("%d\n", mx);
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();return 0;
}

Problem I. KD-Graph

题目大意

T T T 组输入,每组输入 n , m , k n, m, k n,m,k 表示 n n n 个点 m m m 条边的有权联通无向图.

接下来 m m m 行,每行三个整数 u , v , w u, v, w u,v,w 表示无向边的两个端点和边权.

一个图被称为 KD 图当且仅当 n n n 个点可以不重不漏地分成 K K K 组且组内两两点之间存在一条路径,满足这条路径的最大边权小于等于 D D D,不同组的每两点则不存在这样的路径.

要求计算最小的非负整数 D D D,若不存在输出 − 1 -1 1.

分析

赛时一开始二分 + BFS,结果 TLE…

换成二分 + 并查集,常数小了点,得到 AC.

代码实现

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;inline int read()
{int x = 0, f = 1;char ch = getchar();while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - '0';ch = getchar();}return x * f;
}typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e5;
const int N = (int)5e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;int n, m, k;struct enode
{int u, v, w;
} Edge[N + 5];int fa[M + 5];
int rk[M + 5];int tofind(int x)
{if(x == fa[x])  return x;return fa[x] = tofind(fa[x]);
}int cal(int lim)
{for(int i = 1; i <= n; ++i) fa[i] = i;for(int i = 1, u, v; i <= m; ++i){u = Edge[i].u, v = Edge[i].v;if(Edge[i].w <= lim){u = tofind(u), v = tofind(v);if(u != v){if(rk[u] > rk[v]) swap(u, v);fa[u] = v, rk[v] = max(rk[v], rk[u] + 1);}}}int cnt = 0;for(int i = 1; i <= n; ++i) if(fa[i] == i)  ++cnt;return cnt;
}void work()
{n = read(), m = read(), k = read();int l = 0, r = 0, mid;for(int i = 1; i <= m; ++i){Edge[i].u = read(), Edge[i].v = read(), Edge[i].w = read();r = max(r, Edge[i].w);}while(l < r){mid = (l + r) >> 1;if(cal(mid) <= k)   r = mid;else                l = mid + 1;}if(cal(r) == k) printf("%d\n", r);else            printf("-1\n");
}int main()
{
//    cout << 5 * log2(inf) * (M + 2 * N) << "\n";
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T = read();for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

Problem J. zoto

题目大意

T T T 组输入,每组输入 n , m n, m n,m 表示二维平面上有 n n n 个整数点和 m m m 次询问.

接下来 n n n 行,每行一个非负整数 f x [ i ] fx[i] fx[i],表示有一个整数点 ( i , f x [ i ] ) (i, fx[i]) (i,fx[i]).

接下来 m m m 行,每行四个整数 x 0 , y 0 , x 1 , y 1 x0, y0, x1, y1 x0,y0,x1,y1 表示查询 ( x 0 , y 0 ) (x0, y0) (x0,y0) ( x 1 , y 1 ) (x1, y1) (x1,y1) 围成的矩阵里有多少个不同的 y y y 值.

1 ≤ T ≤ 5 , 1 ≤ n , m , x 0 , x 1 ≤ 1 0 5 , 0 ≤ f x [ i ] , y 0 , y 1 ≤ 1 0 5 1 \leq T \leq 5, 1 \leq n, m, x0, x1 \leq 10^5, 0 \leq fx[i], y0, y1 \leq 10^5 1T5,1n,m,x0,x1105,0fx[i],y0,y1105.

分析

一开始用莫队 + 线段树喜提 TLE.

正解是莫队 + 分块.

分块支持 O ( 1 ) O(1) O(1) 修改 + O ( 1 0 5 ) O(\sqrt{10^5}) O(105 ) 查询.

小收获:莫队的修改次数很多,查询次数较少,应选择修改较快的数据结构进行维护.

代码实现

#include <bits/stdc++.h>
using namespace std;inline int read()
{int x = 0, f = 1;char ch = getchar();while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - '0';ch = getchar();}return x * f;
}void print(int n)
{if(n < 0){putchar('-');print(-n);}else{if(n > 9) print(n / 10);putchar(n % 10 + '0');}
}typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e5;
const int N = (int)320;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;int n, m, xblock, yblock, yblockCnt;
int a[M + 5];
int ans[M + 5];struct qnode
{int l, r, a, b, id;bool operator<(const qnode& b)const{return l / xblock == b.l / xblock ? (r == b.r ? 0 : ((l / xblock) & 1) ^ (r < b.r)) : l < b.l;}
} q[M + 5];int id[M + 5];
int num[M + 5];
int nums[N + 5];
int mx;void init()
{yblock = (int)sqrt(mx), yblockCnt = (mx + yblock) / yblock;for(int i = 0; i <= mx; ++i){id[i] = i / yblock;num[i] = 0;}for(int i = 0; i < yblockCnt; ++i) nums[i] = 0;
}void add(int x)
{if(++num[a[x]] == 1) ++nums[id[a[x]]];
}void del(int x)
{if(--num[a[x]] == 0) --nums[id[a[x]]];
}int query(int l, int r)
{int ans = 0;if(id[r] - id[l] + 1 <= 2){for(int i = l; i <= r; ++i) if(num[i]) ++ans;}else{for(int i = l; i <= n && id[i] == id[l]; ++i) if(num[i]) ++ans;for(int i = r; i >= 1 && id[i] == id[r]; --i) if(num[i]) ++ans;for(int i = id[l] + 1; i < id[r]; ++i) ans += nums[i];}return ans;
}void work()
{n = read(), m = read(); xblock = (int)sqrt(n);for(int i = 1; i <= n; ++i) a[i] = read();mx = 1;for(int i = 1; i <= m; ++i){q[i].l = read(), q[i].a = read(), q[i].r = read(), q[i].b = read();mx = max(mx, q[i].b);q[i].id = i;}init();sort(q + 1, q + m + 1);int l = 1, r = 0;for(int i = 1; i <= m; ++i){while(r < q[i].r) add(++r);while(r > q[i].r) del(r--);while(l < q[i].l) del(l++);while(l > q[i].l) add(--l);ans[q[i].id] = query(q[i].a, q[i].b);}for(int i = 1; i <= m; ++i) print(ans[i]), putchar('\n');
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

Problem K. Necklace of Beads

题目大意

T T T 组输入,每组给出 n n n k k k.

有红蓝绿三种颜色的珠子,红蓝珠子有无穷多个,绿色珠子只有 k k k 个,要求串成 n n n 个珠子的项链.

两个项链如果可以通过旋转得到则认为是一种方案.

求方案数.

1 ≤ T ≤ 10 , 1 ≤ n , k ≤ 1 0 6 1 \leq T \leq 10, 1 \leq n, k \leq 10^6 1T10,1n,k106.

分析

显然Burnside 引理,于是

A N S = 1 n ∑ i = 0 n − 1 ∑ j = 0 ⌊ k n / g c d ( n , i ) ⌋ f ( g c d ( n , i ) , j ) = 1 n ∑ d = 1 n φ ( n d ) ∑ j = 0 ⌊ k n / d ⌋ f ( d , j ) \begin{aligned} ANS &= \frac{1}{n}\sum_{i=0}^{n - 1}{\sum_{j=0}^{\lfloor \frac{k}{n/gcd(n, i)} \rfloor}{f(gcd(n, i), j)}} \\ &= \frac{1}{n}\sum_{d=1}^{n}{\varphi(\frac{n}{d})\sum_{j=0}^{\lfloor \frac{k}{n/d} \rfloor}{f(d, j)}} \end{aligned} ANS=n1i=0n1j=0n/gcd(n,i)kf(gcd(n,i),j)=n1d=1nφ(dn)j=0n/dkf(d,j)

f ( n , m ) f(n, m) f(n,m) 表示长度为 n n n 的环,绿色珠子有 m m m 个的合法方案数.

g ( n , m ) g(n, m) g(n,m) 表示长度为 n n n 的序列,选 m m m 个互不相邻位置的方案数.

对绿色珠子的位置分类讨论:

  1. 假设绿色珠子放在 1 1 1 号位置,则 2 , n 2, n 2,n 这两个位置不能放绿色珠子,并且绿色珠子把环分割为 m m m 段,每段的方案数为 2 2 2,因此总方案数为 g ( n − 3 , m − 1 ) × 2 m g(n - 3, m - 1) \times 2^{m} g(n3,m1)×2m.
  2. 假设绿色珠子放在 n n n 号位置,根据对称性,方案数同 1. 1. 1.
  3. 除上述两种情况之外,方案数为 g ( n − 2 , m ) × 2 m g(n - 2, m) \times 2^{m} g(n2,m)×2m.

因此 f ( n , m ) = g ( n − 3 , m − 1 ) × 2 m + 1 + g ( n − 2 , m ) × 2 m f(n, m) = g(n - 3, m - 1) \times 2^{m+1} + g(n - 2, m) \times 2^{m} f(n,m)=g(n3,m1)×2m+1+g(n2,m)×2m.

由插空法可得 g ( n , m ) = ( n − m + 1 m ) g(n, m) = \binom{n - m + 1}{m} g(n,m)=(mnm+1).

O ( 1 0 6 ) O(10^6) O(106) 预处理出欧拉函数和阶乘,逆元,阶乘逆元.

时间复杂度 O ( 1 0 6 + T n ) O(10^6 + T\sqrt{n}) O(106+Tn ).

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int M = (int)1e6;
const int N = (int)1e5;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;bool is_prime[M + 5];
int cnt, prime[M + 5];
int phi[M + 5];
int fac[M + 5], inv[M + 5], invfac[M + 5];void init()
{phi[1] = 1;memset(is_prime, 1, sizeof(is_prime));cnt = is_prime[0] = is_prime[1] = 0;fac[0] = fac[1] = inv[1] = invfac[0] = invfac[1] = 1;for(int i = 2; i <= M; ++i){fac[i] = 1ll * fac[i - 1] * i % mod;inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;invfac[i] = 1ll * invfac[i - 1] * inv[i] % mod;if(is_prime[i]){prime[++cnt] = i;phi[i] = i - 1;}for(int j = 1; j <= cnt && i * prime[j] <= M; ++j){is_prime[i * prime[j]] = 0;if(i % prime[j] == 0){phi[i * prime[j]] = phi[i] * prime[j];break;}phi[i * prime[j]] = phi[i] * (prime[j] - 1);}}
}int c(int n, int m)
{if(n < 0 || m < 0 || n < m) return 0;return 1ll * fac[n] * invfac[m] % mod * invfac[n - m] % mod;
}void work()
{int n, k; scanf("%d %d", &n, &k);int ans = 0;for(int d = 1, two, r, f, dd; 1ll * d * d <= n; ++d){if(n % d)   continue;two = 1, r = 1ll * k * d / n, f = (!(d&1)) * 2;for(int j = 1; j <= r; ++j){two = 2ll * two% mod;f = (f + 2ll * two * c(d - j - 1, j - 1) + 1ll * two * c(d - j - 1, j)) % mod;}ans = (ans + 1ll * f * phi[n / d]) % mod;if(d * d == n)  continue;dd = n / d;two = 1, r = 1ll * k * dd / n, f = (!(dd&1)) * 2;for(int j = 1; j <= r; ++j){two = 2ll * two% mod;f = (f + 2ll * two * c(dd - j - 1, j - 1) + 1ll * two * c(dd - j - 1, j)) % mod;}ans = (ans + 1ll * f * phi[n / dd]) % mod;}ans = 1ll * ans * inv[n] % mod;printf("%d\n", ans);
}int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0); cout.tie(0);init();int T; scanf("%d", &T);for(int ca = 1; ca <= T; ++ca){
//        printf("Case #%d:\n", ca);work();}
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";return 0;
}

这篇关于2021“MINIEYE杯”中国大学生算法设计超级联赛(1)【解题报告】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/861628

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

Python中的可视化设计与UI界面实现

《Python中的可视化设计与UI界面实现》本文介绍了如何使用Python创建用户界面(UI),包括使用Tkinter、PyQt、Kivy等库进行基本窗口、动态图表和动画效果的实现,通过示例代码,展示... 目录从像素到界面:python带你玩转UI设计示例:使用Tkinter创建一个简单的窗口绘图魔法:用

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

跨国公司撤出在华研发中心的启示:中国IT产业的挑战与机遇

近日,IBM中国宣布撤出在华的两大研发中心,这一决定在IT行业引发了广泛的讨论和关注。跨国公司在华研发中心的撤出,不仅对众多IT从业者的职业发展带来了直接的冲击,也引发了人们对全球化背景下中国IT产业竞争力和未来发展方向的深思。面对这一突如其来的变化,我们应如何看待跨国公司的决策?中国IT人才又该如何应对?中国IT产业将何去何从?本文将围绕这些问题展开探讨。 跨国公司撤出的背景与