// 大数加法
string largeadd(string &a, string &b)
{
if (a.size() < b.size())
swap(a, b);
int p = 0;
for (size_t i = 0; i < b.size(); i++)
{
int ai = a[a.size() - i - 1] - '0';
int bi = b[b.size() - i - 1] - '0';
int sum = ai + bi + p;
if (sum >= 10)
{
p = 1;
sum -= 10;
}
else
p = 0;
a[a.size() - 1 - i] = sum + '0';
}
for (size_t i = b.size(); i < a.size(); i++)
{
int ai = a[a.size() - i - 1] - '0';
if (ai == '9' && p == 1)
{
a[a.size() - i - 1] = '0';
}
else
{
a[a.size() - i - 1] = ai + p + '0';
p = 0;
}
}
if (p == 1)
a.insert(a.begin(), '1');
return a;
}
// 大数减法
string largemin(string a, string b)
{
int flag = 0;
if (b.size() >= a.size() && b >= a)
{
swap(a, b);
flag = 1;
}
int p = 0;
for (size_t i = 0; i < b.size(); i++)
{
int ai = a[a.size() - 1 - i] - '0';
int bi = b[b.size() - 1 - i] - '0';
int diff = ai - bi - p;
if (diff < 0)
{
p = 1;
diff += 10;
}
else
p = 0;
a[a.size() - i - 1] = diff + '0';
}
for (size_t i = b.size(); i < a.size(); i++)
{
int ai = a[a.size() - i - 1] - '0';
if (ai == 0 && p == 1)
{
a[a.size() - i - 1] = '9';
}
else
{
a[a.size() - i - 1] = ai - p + '0';
p = 0;
}
}
while (*a.begin() == '0' && a.size() > 1)
a.erase(a.begin());
if (flag)
a.insert(a.begin(), '-');
return a;
}
// DSU并查集
class DSU
{
private:
vector<int> parent, rank, size;
int count;
public:
DSU(int n) : parent(n + 1), rank(n + 1, 0), size(n + 1, 1), count(n)
{
iota(all(parent), 0ll);
}
int find(int x)
{
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
void merge(int i, int j)
{
int ri = find(i), rj = find(j);
if (ri == rj)
{
return;
}
if (rank[ri] < rank[rj])
{
swap(ri, rj);
}
parent[rj] = ri;
size[ri] += size[rj];
if (rank[ri] == rank[rj])
{
rank[ri]++;
}
count--;
}
bool issame(int i, int j)
{
return find(i) == find(j);
}
int getsize(int x)
{
return size[find(x)];
}
int getgroups()
{
return count;
}
};
// DSU end
// COMB 组合数 快速幂,乘法逆元 都在里面
const int N = 1e6;
vector<int> f(N), invf(N);
bool inited = 0;
int ksm(int base, int exp)
{
int ans = 1;
while (exp)
{
if (exp & 1)
{
ans = ans * base % MOD;
}
base = base * base % MOD;
exp >>= 1;
}
return ans;
}
int inv(int x)
{
return ksm(x, MOD - 2) % MOD;
}
void pre()
{
if (inited)
{
return;
}
inited = 1;
f[0] = 1;
for (int i = 1; i < N; i++)
{
f[i] = f[i - 1] * i % MOD;
}
invf[N - 1] = inv(f[N - 1]);
for (int i = N - 2; i >= 0; i--)
{
invf[i] = invf[i + 1] * (i + 1) % MOD;
}
}
int comb(int n, int k)
{
if (!inited)
{
pre();
}
if (k < 0 or k > n)
{
return 0;
}
return f[n] * invf[k] % MOD * invf[n - k] % MOD;
}
// comb end ----
// 马拉车
// 预处理
string preprocess(const string &s)
{
string t = "^";
for (char c : s)
{
t += "#" + string(1, c);
}
t += "#$";
return t;
}
string longestPalindrome(const string &s)
{
string T = preprocess(s);
int n = T.size();
vector<int> P(n, 0); // P[i] 记录以 t[i] 为中心的回文半径
int C = 0, R = 0; // C 是回文中心,R 是回文串的最右边界
for (size_t i = 1; i < n - 1; i++)
{
// 确定对称位置
int Mirror = 2 * C - i;
if (i < R)
{
P[i] = min(P[Mirror], R - i);
}
// 尝试扩展边界
while (T[i + 1 + P[i]] == T[i - 1 - P[i]])
{
P[i]++;
}
// 如果当前回文串扩展超过了 R,更新中心和右边界
if (i + P[i] > R)
{
C = i;
R = i + P[i];
}
}
// 找到最长的回文子串
int maxLen = 0;
int Centerindex = 0;
for (size_t i = 0; i < n - 1; i++)
{
if (P[i] > maxLen)
{
maxLen = P[i];
Centerindex = i;
}
}
// 构造回文串
int start = (Centerindex - maxLen) / 2;
return s.substr(start, maxLen);
}
signed main()
{
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string s;
cin >> s;
cout << longestPalindrome(s) << endl;
return 0;
}
// end ---
// BIT 树状数组
class BIT
{
private:
vector<int> c;
int n;
int lowbit(int x)
{
return x & -x;
}
public:
BIT(int size) : n(size)
{
c.resize(n + 1, 0);
}
BIT(vector<int> &arr) : n(arr.size())
{
c.resize(n + 1, 0);
for (int i = 1; i <= n; i++)
{
add(i, arr[i - 1]);
}
}
void add(int i, int val)
{
while (i <= n)
{
c[i] += val;
i += lowbit(i);
}
}
int sum(int i)
{
int ans = 0;
while (i)
{
ans += c[i];
i -= lowbit(i);
}
return ans;
}
int query(int l, int r)
{
return sum(r) - sum(l - 1);
}
int get(int i)
{
return query(i, i);
}
int update(int i, int val)
{
auto delta = val = get(i);
add(i, delta);
}
void clear()
{
fill(all(c), 0);
}
// 获取树状数组的大小
int size()
{
return n;
}
};
// BIT end
// 大数模下幂运算
// 快速幂函数,计算 base^exponent mod mod
long long modPow(long long base, long long exponent, long long mod)
{
long long result = 1;
base %= mod;
while (exponent > 0)
{
if (exponent & 1)
result = (result * base) % mod;
base = (base * base) % mod;
exponent >>= 1;
}
return result;
}
void solve()
{
long long x, p;
string y;
cin >> x >> y >> p;
// 对 x 取模(防止数字过大)
x %= p;
// 特判:若 y 为 "0",根据数学通常规定 x^0 = 1(注意:0^0一般认为1也可以根据题意而定)
if (y == "0")
{
cout << 1 % p << "\n";
return;
}
// 答案初始为 1
long long ans = 1;
// 对指数 y 的每一位进行处理
for (char c : y)
{
int d = c - '0';
// 将当前答案提升 10 次方后再乘上 x^d mod p
ans = modPow(ans, 10, p);
ans = (ans * modPow(x, d, p)) % p;
}
cout << ans % p << "\n";
}
// 大树模下 end ---
// 很多DP
// --------------------- 基础DP问题 ---------------------
// 01背包问题 - 每个物品最多选一次
int knapsack01(const vector<int> &weights, const vector<int> &values, int capacity)
{
int n = weights.size();
vector<int> dp(capacity + 1, 0);
for (int i = 0; i < n; i++)
{
for (int j = capacity; j >= weights[i]; j--)
{
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);
}
}
return dp[capacity];
}
// 完全背包问题 - 每个物品可以选无限次
int knapsackComplete(const vector<int> &weights, const vector<int> &values, int capacity)
{
int n = weights.size();
vector<int> dp(capacity + 1, 0);
for (int i = 0; i < n; i++)
{
for (int j = weights[i]; j <= capacity; j++)
{
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);
}
}
return dp[capacity];
}
// 多重背包问题 - 每个物品有特定数量限制
int knapsackMultiple(const vector<int> &weights, const vector<int> &values,
const vector<int> &counts, int capacity)
{
int n = weights.size();
vector<int> dp(capacity + 1, 0);
for (int i = 0; i < n; i++)
{
// 二进制优化 - 将k个物品拆成二进制表示
int w = weights[i], v = values[i], c = counts[i];
for (int k = 1; k <= c; k *= 2)
{
for (int j = capacity; j >= k * w; j--)
{
dp[j] = max(dp[j], dp[j - k * w] + k * v);
}
c -= k;
}
if (c > 0)
{
for (int j = capacity; j >= c * w; j--)
{
dp[j] = max(dp[j], dp[j - c * w] + c * v);
}
}
}
return dp[capacity];
}
// 最长递增子序列 (LIS)
int longestIncreasingSubsequence(const vector<int> &nums)
{
int n = nums.size();
if (n == 0)
return 0;
vector<int> dp(n, 1);
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (nums[i] > nums[j])
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
return *max_element(dp.begin(), dp.end());
}
// 最长递增子序列优化版 (O(nlogn))
int longestIncreasingSubsequenceOptimized(const vector<int> &nums)
{
int n = nums.size();
if (n == 0)
return 0;
vector<int> tails;
for (int num : nums)
{
auto it = lower_bound(tails.begin(), tails.end(), num);
if (it == tails.end())
{
tails.push_back(num);
}
else
{
*it = num;
}
}
return tails.size();
}
// 最长公共子序列 (LCS)
int longestCommonSubsequence(const string &text1, const string &text2)
{
int m = text1.size(), n = text2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (text1[i - 1] == text2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
// 编辑距离
int editDistance(const string &word1, const string &word2)
{
int m = word1.size(), n = word2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 0; i <= m; i++)
dp[i][0] = i;
for (int j = 0; j <= n; j++)
dp[0][j] = j;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (word1[i - 1] == word2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1];
}
else
{
dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;
}
}
}
return dp[m][n];
}
// --------------------- 区间DP ---------------------
// 石子合并问题 - 求合并代价最小值
int mergeStones(const vector<int> &stones)
{
int n = stones.size();
if (n <= 1)
return 0;
// 前缀和加速区间求和
vector<int> prefixSum(n + 1, 0);
for (int i = 0; i < n; i++)
{
prefixSum[i + 1] = prefixSum[i] + stones[i];
}
// dp[i][j] 表示合并区间[i,j]的最小代价
vector<vector<int>> dp(n, vector<int>(n, INF));
// 初始化:单个石子无需合并
for (int i = 0; i < n; i++)
{
dp[i][i] = 0;
}
// 按区间长度递推
for (int len = 2; len <= n; len++)
{
for (int i = 0; i <= n - len; i++)
{
int j = i + len - 1;
// 尝试在不同位置分割
for (int k = i; k < j; k++)
{
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + prefixSum[j + 1] - prefixSum[i]);
}
}
}
return dp[0][n - 1];
}
// --------------------- 树形DP ---------------------
// 树的最大独立集 (在树上选择不相邻的节点使得权值和最大)
void dfsIndependentSet(int node, int parent, const vector<vector<int>> &tree,
const vector<int> &values, vector<vector<int>> &dp)
{
for (int child : tree[node])
{
if (child != parent)
{
dfsIndependentSet(child, node, tree, values, dp);
// dp[node][0] - 不选当前节点
dp[node][0] += max(dp[child][0], dp[child][1]);
// dp[node][1] - 选当前节点
dp[node][1] += dp[child][0];
}
}
dp[node][1] += values[node]; // 加上当前节点的权值
}
int maxIndependentSet(const vector<vector<int>> &tree, const vector<int> &values)
{
int n = values.size();
// dp[i][0] - 不选节点i的最大值, dp[i][1] - 选节点i的最大值
vector<vector<int>> dp(n, vector<int>(2, 0));
dfsIndependentSet(0, -1, tree, values, dp);
return max(dp[0][0], dp[0][1]);
}
// --------------------- 状态压缩DP ---------------------
// 旅行商问题 (TSP)
int tsp(const vector<vector<int>> &dist)
{
int n = dist.size();
// dp[mask][i] 表示已经访问的城市集合为mask,当前在城市i的最短路径
vector<vector<int>> dp(1 << n, vector<int>(n, INF));
// 起点为城市0
dp[1][0] = 0; // 只访问城市0的状态
for (int mask = 1; mask < (1 << n); mask++)
{
for (int i = 0; i < n; i++)
{
if ((mask >> i) & 1)
{ // 城市i已访问
for (int j = 0; j < n; j++)
{
if ((mask >> j) & 1 && i != j)
{ // 城市j也已访问
dp[mask][i] = min(dp[mask][i], dp[mask ^ (1 << i)][j] + dist[j][i]);
}
}
}
}
}
// 所有城市都访问并回到起点
int result = INF;
for (int i = 1; i < n; i++)
{
if (dist[i][0] != INF)
{
result = min(result, dp[(1 << n) - 1][i] + dist[i][0]);
}
}
return result;
}
// --------------------- 数位DP ---------------------
// 计算区间[l,r]中满足条件的数字个数
int digitDP(int l, int r)
{
vector<int> digits;
// 数位分解函数
auto getDigits = [&](int num)
{
vector<int> result;
while (num)
{
result.push_back(num % 10);
num /= 10;
}
reverse(result.begin(), result.end());
return result;
};
// DP函数,自行定义状态和转移方程
function<int(int, bool, bool, int)> dp = [&](int pos, bool isLimit, bool isNum, int state)
{
// 实现特定问题的状态转移逻辑
// pos: 当前处理到的位置
// isLimit: 是否有上界限制
// isNum: 前面是否已经有数字了
// state: 问题相关的状态
if (pos == digits.size())
{
return isNum ? 1 : 0; // 根据具体问题调整返回值
}
// 这里补充具体问题的状态转移逻辑
return 0; // 占位返回
};
// 计算[0,r]的结果
digits = getDigits(r);
int right_result = dp(0, true, false, 0);
// 计算[0,l-1]的结果
digits = getDigits(l - 1);
int left_result = dp(0, true, false, 0);
// 返回区间结果
return right_result - left_result;
}
// --------------------- 概率DP ---------------------
// 骰子问题 - 投掷n个骰子,点数和为target的概率
double diceProb(int n, int target)
{
if (target < n || target > 6 * n)
return 0.0;
vector<vector<double>> dp(n + 1, vector<double>(6 * n + 1, 0.0));
// 初始化:一个骰子
for (int i = 1; i <= 6; i++)
{
dp[1][i] = 1.0 / 6.0;
}
for (int i = 2; i <= n; i++)
{
for (int j = i; j <= 6 * i; j++)
{
for (int k = 1; k <= 6 && k < j; k++)
{
dp[i][j] += dp[i - 1][j - k] * (1.0 / 6.0);
}
}
}
return dp[n][target];
}
// --------------------- 实用DP子问题 ---------------------
// 最大子序和 (Kadane算法)
int maxSubArray(const vector<int> &nums)
{
int currMax = nums[0], globalMax = nums[0];
for (int i = 1; i < nums.size(); i++)
{
currMax = max(nums[i], currMax + nums[i]);
globalMax = max(globalMax, currMax);
}
return globalMax;
}
// 最大子矩阵和
int maxSubMatrix(const vector<vector<int>> &matrix)
{
if (matrix.empty() || matrix[0].empty())
return 0;
int rows = matrix.size(), cols = matrix[0].size();
int result = INT_MIN;
for (int left = 0; left < cols; left++)
{
vector<int> tempSum(rows, 0);
for (int right = left; right < cols; right++)
{
// 将二维问题转化为一维
for (int i = 0; i < rows; i++)
{
tempSum[i] += matrix[i][right];
}
// 应用Kadane算法找一维最大子序和
int kadaneMax = tempSum[0];
int currMax = tempSum[0];
for (int i = 1; i < rows; i++)
{
currMax = max(tempSum[i], currMax + tempSum[i]);
kadaneMax = max(kadaneMax, currMax);
}
result = max(result, kadaneMax);
}
}
return result;
}
// 最长回文子串
string longestPalindrome(const string &s)
{
int n = s.size();
if (n == 0)
return "";
vector<vector<bool>> dp(n, vector<bool>(n, false));
int start = 0, maxLen = 1;
// 所有长度为1的子串都是回文
for (int i = 0; i < n; i++)
{
dp[i][i] = true;
}
// 检查长度为2的子串
for (int i = 0; i < n - 1; i++)
{
if (s[i] == s[i + 1])
{
dp[i][i + 1] = true;
start = i;
maxLen = 2;
}
}
// 检查长度大于2的子串
for (int len = 3; len <= n; len++)
{
for (int i = 0; i <= n - len; i++)
{
int j = i + len - 1;
if (s[i] == s[j] && dp[i + 1][j - 1])
{
dp[i][j] = true;
start = i;
maxLen = len;
}
}
}
return s.substr(start, maxLen);
}
// 很多DP end
// 拓扑
void solve()
{
int n, m;
cin >> n >> m;
unordered_map<int, vector<int>> adj; // 邻接表
vector<int> indegree(n, 0); // 统计入度
vector<int> result; // 结果集合
int s, t;
while (m--)
{
cin >> s >> t;
indegree[t]++;
adj[s].push_back(t);
}
queue<int> que;
for (int i = 0; i < n; i++)
{
// 入度为0 可以作为开头 加入队列
if (!indegree[i])
{
que.push(i);
}
}
while (que.size())
{
auto cur = que.front();
que.pop();
result.push_back(cur);
if (adj[cur].size())
{
for (auto &&i : adj[cur])
{
indegree[i]--;
if (indegree[i] == 0)
{
que.push(i);
}
}
}
}
if (result.size() - n)
{
cout << -1;
}
else
{
cout << result;
}
}
// 拓扑end
// 生成树 kru
void solve()
{
int n, m;
cin >> n >> m;
struct Edge
{
int v1, v2, val;
Edge(int v1, int v2, int val) : v1(v1), v2(v2), val(val) {};
};
vector<Edge> edges;
int v1, v2, val;
while (m--)
{
cin >> v1 >> v2 >> val;
auto [a, b] = minmax(v1, v2);
edges.emplace_back(a, b, val);
}
ranges::sort(edges, [](Edge &a, Edge &b)
{ return a.val < b.val; });
// FU begin
vector<int> father;
auto init = [&](int n)
{
father.resize(n + 1);
iota(father.begin(), father.end(), 0ll);
};
function<int(int)> ffind = [&](int i)
{
return i == father[i] ? i : ffind(father[i]);
};
auto funion = [&](int i, int j)
{
auto ri = ffind(i), rj = ffind(j);
if (ri != rj)
{
father[ri] = rj;
}
};
auto issame = [&](int i, int j) -> bool
{
return ffind(i) == ffind(j);
};
// FU end
init(n);
int MSTweight = 0, MSTnode = 0;
for (auto &&[v1, v2, val] : edges)
{
if (!issame(v1, v2))
{
funion(v1, v2);
MSTweight += val;
MSTnode++;
}
if (MSTnode == n - 1)
{
cout << MSTnode;
return;
}
}
cout << -1;
return;
}
// 生成树 kru end
// 生成树 prim
void solve()
{
int v, e;
cin >> v >> e;
int vsz = v + 1;
vector<vector<pair<int, int>>> adj(vsz + 1);
int v1, v2, val;
while (e--)
{
cin >> v1 >> v2 >> val;
adj[v1].emplace_back(v2, val);
adj[v2].emplace_back(v1, val);
}
// prim begin
vector<int> visited(vsz, 0), minDist(vsz, INFLL);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
minDist[1] = 0;
pq.push({0, 1});
int totalweight = 0, nodeInMST = 0;
while (pq.size())
{
auto [w, cur] = pq.top();
pq.pop();
if (visited[cur])
{
continue;
}
visited[cur] = 1;
totalweight += w;
nodeInMST++;
for (auto &&[next, nextw] : adj[cur])
{
if (visited[next])
{
continue;
}
if (nextw < minDist[next])
{
minDist[next] = nextw;
pq.push({minDist[next], next});
}
}
}
if (nodeInMST != v)
{
cout << -1;
return;
}
cout << totalweight;
}
// prim end
// ford 可以处理负环
void solve()
{
int n, m, s, t, v;
cin >> n >> m;
unordered_map<int, unordered_map<int, int>> adj;
while (m--)
{
cin >> s >> t >> v;
adj[s][t] = v;
}
vector<int> minDist(n + 1, INF);
minDist[1] = 0;
int _ = n - 1;
while (_--) //
{
for (auto &&[v1, v] : adj)
{
for (auto &&[v2, val] : v)
{
if (minDist[v1] != INF)
{
minDist[v2] = min(minDist[v2], minDist[v1] + val);
}
}
}
}
if (minDist[n] == INF)
{
cout << "unconnected";
}
else
{
cout << minDist[n];
}
}
// ford end
// dijk
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<pair<int, int>>> adj(n + 1); // 邻接表,pair<节点, 距离>
int s, e, v;
while (m--)
{
cin >> s >> e >> v;
adj[s].push_back({e, v});
}
/*
堆优化版Dijkstra算法:
1. 使用优先队列维护当前所有可达但未访问的节点,按距离排序
2. 每次取出队列中距离最小的节点,标记为已访问
3. 更新该节点邻居的距离,并将新的可能路径加入队列
*/
vector<int> minDist(n + 1, INT_MAX); // 每一个节点到源点的最小距离
vector<bool> visited(n + 1, false); // 记录节点是否已被访问
int start = 1, en = n;
minDist[start] = 0;
// 优先队列,存储<距离, 节点编号>,按距离从小到大排序
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.push({0, start});
while (!pq.empty())
{
int dist = pq.top().first;
int cur = pq.top().second;
pq.pop();
if (visited[cur])
continue; // 如果已经访问过,跳过
visited[cur] = true; // 标记为已访问
// 更新当前节点的所有邻居
for (const auto &edge : adj[cur])
{
int next = edge.first;
int weight = edge.second;
if (!visited[next] && minDist[cur] + weight < minDist[next])
{
minDist[next] = minDist[cur] + weight;
pq.push({minDist[next], next});
}
}
}
cout << (minDist[en] == INT_MAX ? -1 : minDist[en]);
}
// dijk end
// 多源最短路 floyd
void solve()
{
int n;
cin >> n;
vector v(n + 1, vector<int>(n + 1, INT_MAX));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> v[i][j];
}
}
// floyd
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
v[i][j] = min(v[i][j], v[i][k] + v[k][j]);
}
}
}
}
// flyd end