# 2021牛客寒假算法基础集训营6【解题报告】

## A 回文括号序列计数

### 题目描述

1. 空串是括号序列。
2. 两个括号序列P和Q的拼接是括号序列。
3. 如果P是括号序列，’(’+P+’)'是括号序列。

T 行。对于每组数据，你的答案。

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e5;
const int N = (int)1e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

void work()
{
int n;
scanf("%d", &n);
if(n)   printf("0\n");
else        printf("1\n");
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
int T; scanf("%d", &T);
while(T--) work();
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## B 系数

### 分析：

f ( x ) = ( x 2 + x + 1 ) n ≡ ( x 2 − 2 x + 1 ) n = ( x − 1 ) 2 n    ( m o d    3 ) f(x) = (x^2 + x + 1)^n \equiv (x^2 - 2x + 1)^n = (x - 1)^{2n} \;(mod \; 3)

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)2e3;
const int N = (int)3e3;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

int fac[] = {1, 1, 2, 0};

int C(int n, int m)
{
if(m > n)   return 0;
return fac[n] * fac[m] * fac[n - m] % 3;
}

int Lucas(ll n, ll m)
{
if(!m)  return 1;
return C(n % 3, m % 3) * Lucas(n / 3, m / 3) % 3;
}

void work()
{
ll n, k; scanf("%lld %lld", &n, &k);
int ans = Lucas(2 * n, k);
if((2 * n - k) & 1) ans = -ans;
ans = (ans % 3 + 3) % 3;
printf("%d\n", ans);
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
int T; scanf("%d", &T);
while(T--) work();
//    work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## C 末三位

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e5;
const int N = (int)1e6;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

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 print(int n)
{
if(n < 10)   printf("0");
if(n < 100)  printf("0");
printf("%d\n", n);
}

void work()
{
int n;
while(~scanf("%d", &n))
{
print(quick(5, n, 1000));
}

}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## D 划数

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e5;
const int N = (int)1e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

{
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;
}

int n, cnt;

void work()
{
while(~scanf("%d %d", &n, &cnt))
{
if(n == 2)
{
int ans = 0;
for(int i = 1, a; i <= n; ++i)
{
scanf("%d", &a);
if(a != cnt || i == 2 && ans == 0) ans = a;
}
printf("%d\n", ans);
}
else
{
int ans = 0;
for(int i = 1, a; i <= n; ++i)
{
scanf("%d", &a);
ans = (ans + a) % 11;
}
ans = ((ans - cnt) % 11 + 11) % 11;
printf("%d\n", ans);
}
}
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## E 网格

### 分析：

“每个位置需要从上下左右四个方向中选择互相垂直的两个” 也就是说每个位置在水平方向选一个方向并在竖直方向选一个方向.

f [ i ] [ j ] [ 0 ] f[i][j][0] 表示点 ( i , j ) (i, j) 选择了左边的第 i i 行最大值， f [ i ] [ j ] [ 1 ] f[i][j][1] 表示点 ( i , j ) (i, j) 选择了右边的第 i i 行最大值.

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e3;
const int N = (int)3e3;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

int a[M + 5][M + 5];
int f[M + 5][2];

inline int w(int n)
{
int ans = n;
while(n)
{
if(n & 1) ++ans;
n >>= 1;
}
return ans;
}

void work()
{
int n, m; scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
scanf("%d", &a[i][j]);
}
}

int ans = 0;

for(int i = 1; i <= n; ++i)
{
f[1][0] = f[1][1] = 0;
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]);
}
ans += max(f[m][0], f[m][1]);
}

for(int i = 1; i <= m; ++i)
{
f[1][0] = f[1][1] = 0;
for(int j = 2; j <= n; ++j)
{
f[j][0] = max(f[j - 1][0], f[j - 1][1] + w(a[j - 1][i]^a[j][i]));
f[j][1] = max(f[j - 1][0], f[j - 1][1]);
}
ans += max(f[n][0], f[n][1]);
}
printf("%d\n", ans);
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## F 组合数问题

### 代码实现：

#include <bits/stdc++.h>

using namespace std;
typedef vector<long long> VI;
typedef long long ll;
const ll mod=998244353;
ll powmod(ll a,ll b)
{
ll res=1;
a%=mod;
assert(b>=0);
for(; b; b>>=1)
{
if(b&1)res=res*a%mod;
a=a*a%mod;
}
return res;
}
namespace linear_seq
{
#define rep(i,a,n) for (long long i=a;i<n;i++)
#define pb push_back
#define SZ(x) ((long long)(x).size())
const long long N=10010;
ll res[N],base[N],_c[N],_md[N];

vector<long long> Md;
void mul(ll *a,ll *b,long long k)
{
rep(i,0,k+k) _c[i]=0;
rep(i,0,k) if (a[i]) rep(j,0,k)
_c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
for (long long i=k+k-1; i>=k; i--) if (_c[i])
rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
rep(i,0,k) a[i]=_c[i];
}
long long solve(ll n,VI a,VI b)
{
// a 系数 b 初值 b[n+1]=a[0]*b[n]+...
//        printf("%d\n",SZ(b));
ll ans=0,pnt=0;
long long k=SZ(a);
assert(SZ(a)==SZ(b));
rep(i,0,k) _md[k-1-i]=-a[i];
_md[k]=1;
Md.clear();
rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
rep(i,0,k) res[i]=base[i]=0;
res[0]=1;
while ((1ll<<pnt)<=n) pnt++;
for (long long p=pnt; p>=0; p--)
{
mul(res,res,k);
if ((n>>p)&1)
{
for (long long i=k-1; i>=0; i--) res[i+1]=res[i];
res[0]=0;
rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
}
}
rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
if (ans<0) ans+=mod;
return ans;
}
VI BM(VI s)
{
VI C(1,1),B(1,1);
long long L=0,m=1,b=1;
rep(n,0,SZ(s))
{
ll d=0;
rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
if (d==0) ++m;
else if (2*L<=n)
{
VI T=C;
ll c=mod-d*powmod(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m) C.pb(0);
rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
L=n+1-L;
B=T;
b=d;
m=1;
}
else
{
ll c=mod-d*powmod(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m) C.pb(0);
rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
++m;
}
}
return C;
}
long long gao(VI a,ll n)
{
VI c=BM(a);
c.erase(c.begin());
rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
}
};

int main()
{
long long n;
while(~scanf("%lld", &n))
{
n /= 4;
printf("%lld\n",linear_seq::gao(VI{1,2,72,992,16512,261632},
n));
}
}


## G 机器人

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef __int128 ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e5;
const int N = (int)1e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

int n, x;
int a[25], b[25];

ll cal()
{
ll ans = x;
for(int i = 1; i <= n; ++i) ans = ans * a[i] + b[i];
return ans;
}

double Rand(double l, double r)
{
return (double)rand() / RAND_MAX * (r - l) + l;
}

ll sa()
{
ll ans = cal();
for(double T = 1e3; T > 1e-2; T *= 0.995)
{
int x = 1, y = 1;
while(x == y)
{
x = rand() % n + 1, y = rand() % n + 1;
}
double dt = cal();
swap(a[x], a[y]), swap(b[x], b[y]);
dt -= cal();
if(exp(-dt / T) < Rand(0, 1)) swap(a[x], a[y]), swap(b[x], b[y]);
}
return cal();
}

void print(ll x)
{
if(x < 0)
{
putchar('-');
print(-x);
}
if(x > 9) print(x / 10);
putchar(x % 10 + '0');
}

int id[25];

ll cal2()
{
ll ans = x;
for(int i = 1; i <= n; ++i) ans = ans * a[id[i]] + b[id[i]];
return ans;
}

void work()
{
scanf("%d %d", &n, &x);
for(int i = 1; i <= n; ++i) scanf("%d %d", &a[i], &b[i]), id[i] = i;
if(n <= 10)
{
ll ans = 0;
do
{
ans = max(ans, cal2());
}while(next_permutation(id + 1, id + n + 1));
print(ans); putchar('\n');
}
else
{
ll ans = 0;
while(1.0 * clock() / CLOCKS_PER_SEC < 2.0) ans = max(ans, sa());
print(ans); putchar('\n');
}
}

/**
2207528421052631578947368420
2207528421052631578947368420
**/

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
srand(time(NULL));
work();
cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## H 动态最小生成树

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)3e4;
const int N = (int)2e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

int n, m, q;
int fa[N + 5];

struct enode
{
int u, v, w;
} Edge[M + 5];

struct tnode
{
int len, w, id[N + 5];
tnode() = default;
tnode(const tnode& b)
{
this->len = b.len;
this->w = b.w;
for(int i = 1; i <= this->len; ++i)
{
this->id[i] = b.id[i];
}
}
} tree[M * 8 + 5];

inline int lc(int k) {return k<<1;}
inline int rc(int k) {return k<<1|1;}

int tmp[M + 5];
tnode ans;

int tofind(int x)
{
if(x == fa[x]) return x;
return fa[x] = tofind(fa[x]);
}

void push_up(tnode& u, const tnode& s1, const tnode& s2, bool f = 0)
{
int len = 0, p1 = 1, p2 = 1;
while(p1 <= s1.len && p2 <= s2.len)
{
if(Edge[s1.id[p1]].w <= Edge[s2.id[p2]].w) tmp[++len] = s1.id[p1++];
else                                       tmp[++len] = s2.id[p2++];
}
while(p1 <= s1.len)   tmp[++len] = s1.id[p1++];
while(p2 <= s2.len)   tmp[++len] = s2.id[p2++];
for(int i = 1; i <= n; ++i) fa[i] = i;
u.len = u.w = 0;
for(int i = 1, a, b; i <= len; ++i)
{
a = tofind(Edge[tmp[i]].u), b = tofind(Edge[tmp[i]].v);
if(a != b)
{
fa[a] = b;
u.id[++u.len] = tmp[i];
u.w += Edge[tmp[i]].w;
}
}
}

void build(int k, int l, int r)
{
if(l == r)
{
tree[k].id[tree[k].len = 1] = l;
tree[k].w = Edge[l].w;
return;
}
int mid = (l + r) >> 1;
build(lc(k), l, mid);
build(rc(k), mid + 1, r);
push_up(tree[k], tree[lc(k)], tree[rc(k)]);
}

void update(int k, int l, int r, int a)
{
if(l == r) return;
int mid = (l + r) >> 1;
if(a <= mid)    update(lc(k), l, mid, a);
else            update(rc(k), mid + 1, r, a);
push_up(tree[k], tree[lc(k)], tree[rc(k)]);
}

void query(int k, int l, int r, int a, int b)
{
if(l >= a && r <= b)
{
push_up(ans, tnode(ans), tree[k], 1);
return;
}
int mid = (l + r) >> 1;
if(a <= mid)    query(lc(k), l, mid, a, b);
if(mid < b)     query(rc(k), mid + 1, r, a, b);
}

void work()
{
scanf("%d %d %d", &n, &m, &q);
for(int i = 1; i <= m; ++i) scanf("%d %d %d", &Edge[i].u, &Edge[i].v, &Edge[i].w);
build(1, 1, m);
int op, x, y, z, t, l, r;
for(int i = 1; i <= q; ++i)
{
scanf("%d", &op);
if(op == 1)
{
scanf("%d %d %d %d", &x, &y, &z, &t);
Edge[x].u = y, Edge[x].v = z, Edge[x].w = t;
update(1, 1, m, x);
}
else
{
scanf("%d %d", &l, &r);
ans.len = ans.w = 0;
query(1, 1, m, l, r);
if(ans.len == n - 1)    printf("%d\n", ans.w);
else                    printf("Impossible\n");
}
}
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## I 贪吃蛇

### 题目描述

PS：每格长1米，贪吃蛇规定不能撞墙，不能咬自己的身体。

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e5;
const int N = (int)1e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

int n, m;
int xs, ys, xe, ye;
char s[N + 5][N + 5];
int vis[N + 5][N + 5];

int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};

struct node
{
int x, y;
node(int _x = 0, int _y = 0): x(_x), y(_y){}
};

queue<node> q;

int bfs(int x, int y)
{
vis[x][y] = 1;
q.push(node(x, y));
while(!q.empty())
{
node u = q.front(); q.pop();
int x = u.x, y = u.y;
if(x == xe && y == ye) return (vis[x][y] - 1) * 100;
for(int i = 0; i < 4; ++i)
{
int xx = x + dx[i], yy = y + dy[i];
if(xx < 1 || xx > n || yy < 1 || yy > m || s[xx][yy] == '#') continue;
if(!vis[xx][yy])
{
vis[xx][yy] = vis[x][y] + 1;
q.push(node(xx, yy));
}
}
}
return -1;
}

void work()
{
scanf("%d %d", &n, &m);
scanf("%d %d %d %d", &xs, &ys, &xe, &ye);
for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
int ans = bfs(xs, ys);
printf("%d\n", ans);
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


## J 天空之城

### 代码实现：

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

const int M = (int)1e5;
const int N = (int)1e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;

int n, q;
int idcnt;
map<string, int> mp;
int fa[5000 + 5];

struct enode
{
int u, v, w;
enode(int _u = 0, int _v = 0, int _w = 0): u(_u), v(_v), w(_w){}
bool operator<(const enode& b)const
{
return w < b.w;
}
} Edge[200000 + 5];

inline int id(const string& s)
{
if(!mp.count(s)) mp[s] = ++idcnt;
return mp[s];
}

int tofind(int x)
{
if(x == fa[x]) return x;
return fa[x] = tofind(fa[x]);
}

void kruskal(int st)
{
ll ans = 0;
sort(Edge + 1, Edge + q + 1);
for(int i = 1, a, b; i <= q; ++i)
{
a = tofind(Edge[i].u);
b = tofind(Edge[i].v);
if(a != b)
{
fa[a] = b;
ans += Edge[i].w;
}
}
for(int i = 1; i <= n; ++i)
{
if(tofind(i) != tofind(st))
{
cout << "No!\n";
return;
}
}
cout << ans << "\n";
assert(idcnt <= n);
}

void work()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
while(cin >> n >> q)
{
mp.clear();
idcnt = 0;
for(int i = 1; i <= n; ++i) fa[i] = i;

string st, a, b; cin >> st; id(st);
for(int i = 1, ida, idb, w; i <= q; ++i)
{
cin >> a >> b >> w;
ida = id(a), idb = id(b);
Edge[i].u = ida, Edge[i].v = idb, Edge[i].w = w;
}
kruskal(id(st));
}
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
//    int T; scanf("%d", &T);
//    while(T--) work();
work();
//    cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}


02-05 1531

02-06 2476
02-03 20
02-10 209
02-17 135
02-07 502
08-06 5万+