本场题目很友好, H H H题还放过了暴力,不过感觉时限开的有点大了。
A、 回文括号序列计数
题意:
求长度为 n n n的回文括号序列数。共 T T T组数据。
数据范围: 1 ≤ n ≤ 1 0 9 , 1 ≤ T ≤ 1 0 6 1\leq n\leq 10^9,1\leq T\leq 10^6 1≤n≤109,1≤T≤106
题解:
括号序列一直都是混淆回文串的一把好手(握拳)。
看到这个数据范围: O ( log n ) O(\log n) O(logn)也很难过,那么极大可能是 O ( 1 ) O(1) O(1)的。
当 n n n为奇数此时已经不满足回文串的要求了。
考虑括号序列的第一个字符一定是左括号,最后一个字符一定是右括号,因此不可能存在非空的括号序列。因此只有空串是回文括号序列。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
void solve() {
scanf("%d", &n);
printf("%d\n", !n);
}
int main()
{
int T = 1;
scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}
B、系数
题意:
求 ( x 2 + x + 1 ) n (x^2+x+1)^n (x2+x+1)n中 x k x^k xk的系数模 3 3 3的结果,
数据范围: 0 ≤ n ≤ 1 0 15 , 0 ≤ k ≤ 2 n 0\leq n\leq 10^{15}, 0\leq k\leq 2n 0≤n≤1015,0≤k≤2n
题解:
一个与题目联系十分紧密的转换:
在模 3 3 3意义下有:
( x 2 + x + 1 ) n = ( x 2 − 2 x + 1 + 3 x ) n = ( x 2 − 2 x + 1 ) n = ( x − 1 ) 2 n (x^2+x+1)^n=(x^2-2x+1+3x)^n=(x^2-2x+1)^n=(x-1)^{2n} (x2+x+1)n=(x2−2x+1+3x)n=(x2−2x+1)n=(x−1)2n
由于模数很小,而 n , k n,k n,k很大,所以直接使用 L u c a s Lucas Lucas即可。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 3;
ll n, k;
ll C(ll a, ll b, ll p) {
if(a < b) return 0;
if(a == b) return 1;
ll ta = 1, tb = 1;
for(ll i = 1, j = a; i <= b; ++i, --j)
ta = ta * j % p, tb = tb * i % p;
//tb的mod - 2在此就是tb
return ta * tb % p;
}
ll Lucas(ll a, ll b, ll p) {
if(a < b) return 0;
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * Lucas(a / p, b / p, p) % p;
}
void solve() {
scanf("%lld%lld", &n, &k);
ll f = (2 * n - k) & 1;
ll res = Lucas(2 * n, k, mod);
if(f == 1) res = (mod - res) % mod;
printf("%lld\n", res);
}
int main()
{
int T = 1;
scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}
C、末三位
题意:
求 5 n 5^n 5n的最后三位,不足三位补 0 0 0。共 T T T组数据。
数据范围: 1 ≤ n ≤ 1 0 9 , 1 ≤ T ≤ 1 0 6 1\leq n\leq 10^9,1\leq T\leq 10^6 1≤n≤109,1≤T≤106
题解:
赛时没有细想,直接上快速幂了。时间复杂度为: O ( T log n ) O(T\log n) O(Tlogn)
由于 5 5 5比较特殊,所以打个表可以发现末三位的循环节为 4 4 4。
这类题大致都可以打表做。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
const int mod = 1000;
int qp(int a, int b) {
int ans = 1;
while(b) {
if(b & 1) ans = (ll)ans * a % mod;
a = (ll)a * a % mod;
b >>= 1;
}
return ans;
}
void solve() {
int ans = qp(5, n);
printf("%03d\n", ans);
}
int main()
{
while(~scanf("%d", &n)) solve();
return 0;
}
D、划数
题意: 给定 n n n个数,第 i i i个数为 a i a_i ai。每次取出两个数相加对 11 11 11取模成为新的数。最后操作剩两个数时,有一个 x x x,一个 y y y,先已知 y y y,且保证 y ≥ 11 y\geq 11 y≥11,求 x x x。共 T T T组。
数据范围: 1 ≤ n ≤ 150000 , 1 ≤ T ≤ 10 , 1 ≤ a i ≤ 1 0 6 1\leq n\leq 150000,1\leq T\leq 10,1\leq a_i\leq 10^6 1≤n≤150000,1≤T≤10,1≤ai≤106
题解:
本题比较坑的点是 n = 2 n=2 n=2时, x x x和 y y y都可能大于等于 11 11 11,否则 x x x必定是取模 11 11 11后的数。特判 n = 2 n=2 n=2即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod = 11;
const int N = 2e5 + 10;
int n, cnt;
int a[N];
void solve() {
int sum = 0;
for(int i = 1, x; i <= n; ++i) scanf("%d", &a[i]);
if(n == 2) {
if(a[1] == cnt) printf("%d\n", a[2]);
else printf("%d\n", a[1]);
return ;
}
for(int i = 1; i <= n; ++i) sum = (sum + a[i]) % mod;
int res = ((sum - cnt) % 11 + 11) % 11;
printf("%d\n", res);
}
int main()
{
while(~scanf("%d%d", &n, &cnt)) solve();
}
E、网格
题意:
给定一个 n × m n\times m n×m的网格,第 i i i行 j j j列上有数字 a i , j a_{i,j} ai,j,每个位置需要向四个方向中取两个方向,且一个格子的两个方向必须垂直。定义 w ( x ) = x + b i t _ c n t ( x ) w(x)=x+bit\_cnt(x) w(x)=x+bit_cnt(x)
b i t _ c n t ( x ) bit\_cnt(x) bit_cnt(x)即 x x x的二进制中 1 1 1的个数。
如果两个相邻位置(即 ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ = 1 |x_1-x_2|+|y_1-y_2|=1 ∣x1−x2∣+∣y1−y2∣=1)互相选择对方的方向,则对答案产生 w ( a x 1 , y 1 ⊕ a x 2 , y 2 ) w(a_{x_1,y_1}\oplus a_{x_2,y_2}) w(ax1,y1⊕ax2,y2)的贡献。
现在问答案最大是多少。
数据范围: 1 ≤ n , m ≤ 1000 , 1 ≤ a i , j ≤ 1024 1\leq n,m\leq 1000,1\leq a_{i,j}\leq 1024 1≤n,m≤1000,1≤ai,j≤1024
题解:
由于一个格子的两个方向垂直,所以整体来看横向和竖向对答案的贡献是独立的。所以我们只需要单独考虑每个方向即可。
现在考虑一行:
f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示第 j j j个格子选择了向左/向右这个方向。
所以状态转移为:
f [ i ] [ j ] [ 0 ] = m a x ( f [ i ] [ j − 1 ] [ 0 ] , f [ i ] [ j − 1 ] [ 1 ] + w ( a [ i − 1 ] [ j ] ⊕ a [ i ] [ j ] ) ) f[i][j][0]=max(f[i][j-1][0],f[i][j-1][1]+w(a[i-1][j]\oplus a[i][j])) f[i][j][0]=max(f[i][j−1][0],f[i][j−1][1]+w(a[i−1][j]⊕a[i][j]))
f [ i ] [ j ] [ 1 ] = m a x ( f [ i ] [ j − 1 ] [ 0 ] , f [ i ] [ j − 1 ] [ 1 ] ) f[i][j][1]=max(f[i][j-1][0],f[i][j-1][1]) f[i][j][1]=max(f[i][j−1][0],f[i][j−1][1])
列同行。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1010;
int a[N][N];
ll f[N][2];
int n, m;
int cnt[1024];
int w(int x) {
if(~cnt[x]) return cnt[x];
int act = 0;
int tx = x;
while(tx) ++act, tx -= (tx & -tx);
return cnt[x] = act + x;
}
int main()
{
memset(cnt, -1, sizeof cnt);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
scanf("%d", &a[i][j]);
ll res = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 2; j <= m; ++j) {
f[j][0] = max(f[j - 1][0], f[j - 1][1] + w(a[i][j - 1] ^ a[i][j]));
f[j][1] = max(f[j - 1][0], f[j - 1][1]);
}
res += max(f[m][0], f[m][1]);
}
for(int j = 1; j <= m; ++j) {
for(int i = 2; i <= n; ++i) {
f[i][0] = max(f[i - 1][0], f[i - 1][1] + w(a[i - 1][j] ^ a[i][j]));
f[i][1] = max(f[i - 1][0], f[i - 1][1]);
}
res += max(f[n][0], f[n][1]);
}
printf("%lld\n", res);
return 0;
}
F、组合数问题
题意:
求 ∑ n i ( i % 4 = = 0 ) × C n i \sum_n^i (i\%4==0)\times C_n^i ∑ni(i%4==0)×Cni。
数据范围: 1 ≤ n ≤ 1 0 18 1\leq n\leq 10^{18} 1≤n≤1018,保证 n n n是 4 4 4的倍数。
题解:
解法一:
赛时是打表做的,可以考虑先取模打表。
n n n | r e s 1 res1 res1 | r e s 2 res2 res2 | 差 值 差值 差值 |
---|---|---|---|
004 | 6 | 2 | -4 |
008 | 56 | 72 | 16 |
012 | 1056 | 992 | -64 |
016 | 16256 | 16512 | 256 |
020 | 262656 | 261632 | -1024 |
024 | 4192256 | 4196352 | 4096 |
028 | 67117056 | 67100672 | -16384 |
032 | 1073709056 | 1073774592 | 65536 |
其中 r e s 2 res2 res2是我们要求的,那么可以发现规律了。
这里 k = n 4 k=\frac{n}{4} k=4n,那么当 k k k为奇数时,差值为负。
由于 r e s 1 + r e s 2 = ∑ n i ( i % 2 = = 0 ) × C n i = 2 n − 1 res1+res2=\sum_n^i(i\%2==0)\times C_n^i=2^{n-1} res1+res2=∑ni(i%2==0)×Cni=2n−1
现在得知 r e s 2 − r e s 1 res2-res1 res2−res1的值,所以可以直接求得 r e s 1 res1 res1。
证明一下:这里 n n n为偶数。
∑ n i ( i % 2 = = 0 ) × C n i = 2 n − 1 \sum_n^i(i\%2==0)\times C_n^i=2^{n-1} ∑ni(i%2==0)×Cni=2n−1
这里有: ( x + y ) n = C n 0 x 0 y n + C n 1 x y n − 1 + . . . + C n n − 1 x n − 1 y + C n n x n y 0 (x+y)^n=C_n^0 x^0y^n+C_n^1xy^{n-1}+...+C_n^{n-1}x^{n-1}y+C_n^nx^ny^0 (x+y)n=Cn0x0yn+Cn1xyn−1+...+Cnn−1xn−1y+Cnnxny0
当 x = 1 , y = 1 x=1,y=1 x=1,y=1, 该二项式值为 C n 0 + C n 1 + . . . + C n n − 1 + C n n = 2 n C_n^0+C_n^1+...+C_n^{n-1}+C_n^n=2^n Cn0+Cn1+...+Cnn−1+Cnn=2n
当 x = 1 , y = − 1 x=1,y=-1 x=1,y=−1,该二项式值为 C n 0 − C n 1 + . . . − C n n − 1 + C n n = 0 C_n^0-C_n^1+...-C_n^{n-1}+C_n^n=0 Cn0−Cn1+...−Cnn−1+Cnn=0
两式相加: 2 ( C n 0 + C n 2 + . . . + C n n ) = 2 n 2(C_n^0+C_n^2+...+C_n^n)=2^n 2(Cn0+Cn2+...+Cnn)=2n,所以得证。
解法二:来自官方题解
大学以来不碰高中数学真是想不到。
考虑复数:
i 1 = i i^1=i i1=i
i 2 = − 1 i^2=-1 i2=−1
i 3 = − i i^3=-i i3=−i
i 4 = 1 i^4=1 i4=1
这里有: ( x + y ) n = C n 0 x 0 y n + C n 1 x y n − 1 + . . . + C n n − 1 x n − 1 y + C n n x n y 0 (x+y)^n=C_n^0 x^0y^n+C_n^1xy^{n-1}+...+C_n^{n-1}x^{n-1}y+C_n^nx^ny^0 (x+y)n=Cn0x0yn+Cn1xyn−1+...+Cnn−1xn−1y+Cnnxny0
当 x = 1 , y = i x=1,y=i x=1,y=i,该二项式为:
( 1 + i ) n = C n 0 i n + C n 1 i n − 1 + C n 2 i n − 2 + C n 3 i n − 3 + . . . + C n n − 1 i + C n n i 0 (1+i)^n=C_n^0 i^n+C_n^1i^{n-1}+C_n^2i^{n-2}+C_n^3i^{n-3}+...+C_n^{n-1} i+C_n^ni^0 (1+i)n=Cn0in+Cn1in−1+Cn2in−2+Cn3in−3+...+Cnn−1i+Cnni0
当 x = 1 , y = − i x=1,y=-i x=1,y=−i,该二项式为:
( 1 − i ) n = C n 0 ( − i ) n + C n 1 ( − i ) n − 1 + . . . + C n n − 1 ( − i ) + C n n ( − i ) 0 (1-i)^n=C_n^0(-i)^n+C_n^1(-i)^{n-1}+...+C_n^{n-1}(-i)+C_n^n (-i)^0 (1−i)n=Cn0(−i)n+Cn1(−i)n−1+...+Cnn−1(−i)+Cnn(−i)0
= C n 0 i n − C n 1 i n − 1 + C n 2 i n − 2 − C n 3 i n − 3 + . . . − C n n − 1 i + C n n i 0 =C_n^0 i^n-C_n^1 i^{n-1}+C_n^2i^{n-2}-C_n^3i^{n-3}+...-C_n^{n-1}i+C_n^ni^0 =Cn0in−Cn1in−1+Cn2in−2−Cn3in−3+...−Cnn−1i+Cnni0
( 1 + i ) n + ( 1 − i ) n = 2 ( C n 0 − C n 2 + C n 4 − C n 6 . . . + C n n ) (1+i)^n+(1-i)^n=2(C_n^0-C_n^2+C_n^4-C_n^6...+C_n^n) (1+i)n+(1−i)n=2(Cn0−Cn2+Cn4−Cn6...+Cnn)
( 1 + 1 ) n + ( 1 − 1 ) n = 2 ( C n 0 + C n 2 + C n 4 + C n 6 . . . + C n n ) (1 +1)^n+(1-1)^n=2(C_n^0+C_n^2+C_n^4+C_n^6...+C_n^n) (1+1)n+(1−1)n=2(Cn0+Cn2+Cn4+Cn6...+Cnn)
所以 ∑ n i ( i % 4 = = 0 ) × C n i = ( 1 + i ) n + ( 1 − i ) n + 2 n 4 \sum_n^i (i\%4==0)\times C_n^i=\frac{(1+i)^n+(1-i)^n+2^n}{4} ∑ni(i%4==0)×Cni=4(1+i)n+(1−i)n+2n
套用一下复数快速幂即可。
代码:
解法一:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
ll n;
ll qp(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = (ll)ans * a % mod;
a = (ll)a * a % mod;
b >>= 1;
}
return ans;
}
void solve() {
scanf("%lld", &n);
ll ano = n / 4;
ll cha = qp(4, ano);
cha = cha * qp(2, mod - 2) % mod;
if(ano & 1) cha *= -1;
ll res = ((qp(2, n - 2) + cha) % mod + mod) % mod;
printf("%lld\n", res);
}
int main()
{
int T = 1;
//scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}
解法二:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
typedef long long ll;
struct Complex {
ll a, b; // a + bi
Complex(ll _a, ll _b) {
a = _a, b = _b;
}
};
Complex mul(Complex A, Complex B) {
ll a = ((A.a * B.a - A.b * B.b) % mod + mod) % mod;
ll b = ((A.a * B.b + A.b * B.a) % mod + mod) % mod;
return Complex(a, b);
}
Complex C_qp(Complex A, ll b) {
Complex ans(1, 0);
while(b) {
if(b & 1) ans = mul(ans, A);
A = mul(A, A);
b >>= 1;
}
return ans;
}
ll qp(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
ll n;
int main()
{
scanf("%lld", &n);
ll res = qp(2, n);
Complex A(1, 1);
Complex B(1, -1);
A = C_qp(A, n);
B = C_qp(B, n);
res = (res + A.a + B.a) % mod;
res = res * qp(4, mod - 2) % mod;
res = (res + mod) % mod;
printf("%lld\n", res);
return 0;
}
G、机器人
题意:
给定 n n n个 a , b a,b a,b,初始有一个 x x x,求一个指定的 n n n个 a , b a,b a,b排列使得最后 a x + b ax+b ax+b最大,前一次的 a i − 1 x + b i − 1 a_{i-1}x+b_{i-1} ai−1x+bi−1作为后一次 a i x + b i a_ix+b_i aix+bi的 x x x。
数据范围: 1 ≤ n , x , a i , b i ≤ 20 1\leq n,x,a_i,b_i\leq 20 1≤n,x,ai,bi≤20
题解:
解法一:
贪心策略:类似于 n o i p 2012 noip2012 noip2012提高的国王游戏贪心策略。
考虑现在已经是使得 x x x最大的 a , b a,b a,b排列。
那么一定有 a i + 1 ( a i x + b i ) + b i + 1 > a i ( a i + 1 x + b i + 1 ) + b i a_{i+1}(a_ix+bi)+b_{i+1}>a_i(a_{i+1}x+b_{i+1})+b_i ai+1(aix+bi)+bi+1>ai(ai+1x+bi+1)+bi
否则对于第 i + 2 i+2 i+2及其之后的操作对应的 x x x都更小。
化简一下得到: a i + 1 b i + b i + 1 > a i b i + 1 + b i a_{i+1}b_i+b_{i+1}>aib_{i+1}+b_i ai+1bi+bi+1>aibi+1+bi
所以整体按照 a i + 1 b i + b i + 1 a_{i+1}b_i+b_{i+1} ai+1bi+bi+1从小到大排序即可。
解法二:
数据范围直接提示是状压 d p dp dp。
考虑状压的转移: d p [ i ] → d p [ i + ( 2 j ) ] dp[i]\to dp[i+(2^j)] dp[i]→dp[i+(2j)]其中 i & 2 j = 0 i \& 2^j=0 i&2j=0
那么具体转移方程为: d p [ i + ( 2 j ) ] = a j × d p [ i ] + b j dp[i+(2^j)]=a_j\times dp[i]+b_j dp[i+(2j)]=aj×dp[i]+bj
代码:
本题考虑到极端是 2 0 20 20^{20} 2020,会爆 l o n g l o n g long\ long long long,可以考虑用 _ _ i n t 128 \_\_int128 __int128或者高精度。
解法一:
#include<bits/stdc++.h>
using namespace std;
#define ptsd puts("")
#define ll __int128
ll read() {
ll 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 output(ll x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) output(x / 10);
putchar(x % 10 + '0');
}
int n;
struct Node {
int x, y;
bool operator < (const Node &A) const {
return x * A.y + y < A.x * y + A.y;
}
}node[30];
int main()
{
ll res = 0;
scanf("%d%d", &n, &res);
for(int i = 1; i <= n; ++i) scanf("%d%d", &node[i].x, &node[i].y);
sort(node + 1, node + n + 1);
for(int i = 1; i <= n; ++i) res = res * node[i].x + node[i].y;
output(res); ptsd;
}
解法二:
#include<bits/stdc++.h>
using namespace std;
#define ll __int128
#define ptsd putchar('\n')
void output(ll x) {
if(x > 9) output(x / 10); putchar(x % 10 + '0'); }
void out(ll x) {
if(x < 0) putchar('-'), x = -x; output(x);}
struct Node {
int a, b;
}node[20];
int n, x;
ll f[1 << 20];
int main()
{
scanf("%d%d", &n, &x);
for(int i = 0; i < n; ++i) scanf("%d%d", &node[i].a, &node[i].b);
f[0] = x;
int last = (1 << n) - 1;
for(int i = 0; i < last; ++i) //last时已经是全1,不会再转移,所以枚举到(1<<n)-1即可
for(int j = 0; j < n; ++j)
if(!(i >> j & 1))
f[i + (1 << j)] = max(f[i + (1 << j)], node[j].a * f[i] + node[j].b);
output(f[last]); ptsd;
return 0;
}
H、动态最小生成树
题意:
初始给定一个 n n n点 m m m边的无向图,每条边都有边权,给定 q q q个操作。
有两种操作:
操作 1 1 1是修改第 x x x条边为连接点为 y , z y,z y,z,边权为 t t t
操作 2 2 2是查询只用编号在 [ l , r ] [l,r] [l,r]内边构造的最小生成树权值,如果不能构造输出 I m p o s s i b l e Impossible Impossible。
数据范围:
1 ≤ n ≤ 200 , 1 ≤ m ≤ 30000 , 1 ≤ q ≤ 30000 1\leq n\leq 200, 1\leq m\leq 30000, 1\leq q\leq 30000 1≤n≤200,1≤m≤30000,1≤q≤30000
1 ≤ u i , v i ≤ n , 1 ≤ w i ≤ 10000 1\leq u_i,v_i\leq n,1\leq w_i\leq 10000 1≤ui,vi≤n,1≤wi≤10000
1 ≤ x ≤ m , 1 ≤ y , z ≤ n , 1 ≤ t ≤ 10000 1\leq x\leq m,1\leq y,z\leq n,1\leq t\leq 10000 1≤x≤m,1≤y,z≤n,1≤t≤10000
题解:
本题时限过大或者说数据范围较小,暴力能过。正解为线段树维护最小生成树边。
I、贪吃蛇
b f s bfs bfs模板
J、天空之城
m s t mst mst模板