A 回文括号序列计数
题目描述
我们定义一个字符串S是回文的,表示S的左右反转和S相同。
我们定义一个字符串是括号序列:
- 空串是括号序列。
- 两个括号序列P和Q的拼接是括号序列。
- 如果P是括号序列,’(’+P+’)'是括号序列。
求长度为 n (0<=n<=10^9) 的回文括号序列的方案数,对 998244353 取膜。
输入描述:
第一行一个 T 表示数据组数。T<=1000000。
接下来 T 行,每行一个 n 。
输出描述:
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 f(x)=(x^2+x+1)^n f(x)=(x2+x+1)n,求它的第 k 项系数。
输入描述:
第一行,输入 T ,表示数据组数。
接下来 T 行,每行输入 n 和 k 。
输出描述:
对于每组询问,输出系数模 3 后的结果。
分析:
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) f(x)=(x2+x+1)n≡(x2−2x+1)n=(x−1)2n(mod3)
因此第 k k k 项的系数等于 ( − 1 ) 2 n − k ( 2 n k ) (-1)^{2n - k}\binom{2n}{k} (−1)2n−k(k2n).
代码实现:
#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 末三位
题目描述
牛牛最近刚学完指数,他理解了 2 2 = 4 , 3 3 = 27 2^2=4,3^3=27 22=4,33=27…
但是,他现在想知道: 5 n 5^n 5n的末三位是多少?
输入描述:
有多组输入数据。
每组数据输入一个数n,表示指数。
输出描述:
输出 5 n 5^n 5n 的末三位。
分析:
直接快速幂.
代码实现:
#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 划数
题目描述
一个智能机器人在黑板上写了n个数,它每次划去任意两个数,并在数列的后面写上这两个数的和对11取模的值。
例:5 6 7 8 9,划去5 6后,数列变为7 8 9 0.
有趣的是,机器人在还剩下两个数的时候突然“罢工”了,已知其中一个数cnt(cnt >= 11),求另外一个数的值。
输入描述:
多组测试数据,以 EOF 结尾。
每组数据,第一行输入n和cnt。
第二行输入n个数,第i个数表示num[i]。
输出描述:
每组数据,输出另外一个数的值。
分析:
小坑点是要特判 n = 2 n = 2 n=2 的情况.
代码实现:
#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 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;
}
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 网格
题目描述
有一个 n 行 m 列的网格,第 i 行 j 列上有数字 a i , j a_{i,j} ai,j。每个位置需要从上下左右四个方向中选择互相垂直的两个。
定义 w(x)=x+popcnt(x) ,其中 popcnt(x) 表示 x 的二进制位中 1 的位的数量。
如果两个相邻的位置 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1,y1),(x2,y2) 互相位于对方选择的某个方向上,则对答案由 w ( a x 1 , y 1 x o r a x 2 , y 2 ) w(a_{x_1,y_1}\ xor\ a_{x_2,y_2}) w(ax1,y1 xor ax2,y2) 的贡献,其中 xor 表示二进制中的按位异或。
小 Z 想问你答案的最大值是多少。
输入描述:
第一行,输入 n,m 。
接下来 n 行 m 列,输入这个网格。
输出描述:
输出答案的最大值。
分析:
“每个位置需要从上下左右四个方向中选择互相垂直的两个” 也就是说每个位置在水平方向选一个方向并在竖直方向选一个方向.
由于在水平方向上选 与 在竖直方向上选是独立且对称的,所以只需要解决如何在水平方向上选.
显然 dp.
设
f
[
i
]
[
j
]
[
0
]
f[i][j][0]
f[i][j][0] 表示点
(
i
,
j
)
(i, j)
(i,j) 选择了左边的第
i
i
i 行最大值,
f
[
i
]
[
j
]
[
1
]
f[i][j][1]
f[i][j][1] 表示点
(
i
,
j
)
(i, j)
(i,j) 选择了右边的第
i
i
i 行最大值.
答案即为 ∑ i = 1 n m a x ( f [ i ] [ m ] [ 0 ] , f [ i ] [ m ] [ 1 ] ) \sum_{i=1}^{n}{max(f[i][m][0], f[i][m][1])} ∑i=1nmax(f[i][m][0],f[i][m][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)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 组合数问题
题目描述
小 M 很喜欢组合数。
小 Z 给了她一个数 n (n为偶数),让她计算 ( n 0 ) + ( n 2 ) + ( n 4 ) . . + ( n n ) \binom{n}{0}+\binom{n}{2}+\binom{n}{4}..+\binom{n}{n} (0n)+(2n)+(4n)..+(nn) ,小 M 一下子就秒掉了,觉得题好简单。
因此,小 Z 给了她一个难题:给定一个数 n (n 是4的倍数),计算 ( n 0 ) + ( n 4 ) + ( n 8 ) + . . . + ( n n ) \binom{n}{0}+\binom{n}{4}+\binom{n}{8}+...+\binom{n}{n} (0n)+(4n)+(8n)+...+(nn),答案对 998244353 取模。
小 M 不会做,请你来帮帮她吧!
输入描述:
输入一个数 n 。
输出描述:
输出答案对 998244353 取模的值。
分析:
杜教筛BM YYDS!(笑
代码实现:
#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 机器人
题目描述
有 n 个机器人,每个机器人会读入一个 x ,并返回 ax+b 。
现在银临姐姐手里有一个数 x ,她想将机器人按某种顺序排列,使得最终返回得到的 x 尽可能大。
但是计算量太大啦,请你编个程序帮帮她吧。
输入描述:
第一行读入 n,x ,接下来 n 行依次输入 a i , b i a_i, b_i ai,bi 。
输出描述:
输出最大值。
分析:
这题可以贪心也可以状压 dp.
因此这里使用了模拟退火(?
代码实现:
#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 动态最小生成树
题目描述
小 Z 喜欢最小生成树。
小 Z 有一张 n n n 个点 m m m 条边的图,每条边连接点 u i , v i u_i,v_i ui,vi ,边权为 w i w_i wi。他想进行 q q q 次操作,有如下两种类型:
修改第
x
x
x 条边为连接点
y
,
z
y,z
y,z ,边权为
t
t
t ;
查询只用编号在
[
l
,
r
]
[l,r]
[l,r] 范围内的边,得到的最小生成树权值是多少。
由于修改和查询量实在是太大了,小 Z 想请你用程序帮他实现一下。
输入描述:
第一行,输入 n,m,q 。
接下来 m 行,输入 u i , v i u_i,v_i ui,vi 。
接下来 q 行,读入 opt ,若 opt=1 则继续读入 x,y,z,t ,否则读入 l,r 。
输出描述:
对于每次询问,输出最小生成树权值。如果无解,输出 Impossible 。
分析:
赛时暴力给艹过去了…
补一下正解(好像是个经典题:动态最小生成树)
线段树维护边,每个节点存的是当前边区间的最小生成树的边
在 push_up 的时候可以用归并排序合并.
代码实现:
#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 贪吃蛇
题目描述
无限增长的贪吃蛇小游戏:
在一个n*m的迷宫中,有一条小蛇,地图中有很多围墙,猥琐的出题者用“#”表示,而可以走的路用“.”表示,小蛇他随机出生在一个点上,出生点表示为“S”,他想抵达的终点表示为“E”,小蛇有一个奇怪的能力,他每走一格便会增长一格,即他走了一格后,他的尾巴不会缩回。
小蛇想知道他怎么到达他想去的地方,请你帮助他。
PS:每格长1米,贪吃蛇规定不能撞墙,不能咬自己的身体。
输入描述:
第一行:输入N,M;
第二行:输入S的坐标Xs,Ys,E的坐标Xe,Ye;
后面的N行:
每行输入M个数,描述每一行的情况。
输出描述:
输出一个数,小蛇到达终点的最短距离(单位:cm),若无法达到,输出-1
分析:
直接 bfs.
代码实现:
#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 天空之城
题目描述
天空之城有5个小镇,名字分别为Ada, Aed, Akk, Orz, Apq,他们也有相互的路径长度。
希达早已期盼着天空之城,如今她登上了天空之城,就想走遍天空之城的每一个城市,但是她希望自己走的路的长度越小越好,以节省体力和节约时间。
巴鲁同意了,但由于他是主力(男孩子嘛),需要帮希达计算出走遍所有城市的最短路径长度。
由于天空之城具有魔力,如果希达想再走一次自己之前走过的路,则她可以在这条路上不花费任何时间。
但是天空之城的城市太多了,他实在计算不过来,只得请你来帮帮忙了。
输入描述:
第一行,输入n,q, 表示有n个城市,q条边;
第二行,输入一个名字tmp,表示希达想要从tmp城市开始行走;
接下来q行,每行输入两个名字a,b和一个数字val, 表示a城市与b城市之间的距离为val.(注意可能有重边和自环)
输出描述:
帮助巴鲁计算出最短的路径长度,如果无法走遍所有城市,输出“No!”。
分析:
最小生成树模板题.
代码实现:
#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;
}