题意:他将布丁摆成一行,接着说:“我可以把某种颜色的布丁全部变成另一种颜色,我还会在某些时刻问你当前一共有多少段颜色。例如:颜色分别为 1,2,2,1 的四个布丁一共有 3 段颜色。”
当把 x 变为 y 时,只有 x 和 y 相邻的地方,才会使段数减少1.
每次合并集合的时候采用按秩合并的方式,把 size 小的集合合并到 size 大的集合中,如果 s z [ 1 ] > s z [ 2 ] sz[1] > sz[2] sz[1]>sz[2],就把对应的两种颜色的指针互换(比如之前 1 → 1 , 2 → 2 1\rightarrow1, 2\rightarrow2 1→1,2→2,现在 1 → 2 , 2 → 1 1\rightarrow2,2\rightarrow1 1→2,2→1).
因为每次都是把小集合并入大集合,因此只需要遍历小的集合每个元素,看看对答案的影响。整个复杂度是 O ( n ∗ l o g n ) O(n*logn) O(n∗logn)
#include
#include
#include
using namespace std;
const int maxn = 100010, maxm = 1000010;
int N, M;
//存储链表,即颜色编号连接的苹果(位置编号)
//每个颜色作为一个头结点,最多maxm,每个位置连一条边,最多maxn条。
int h[maxm], e[maxn], ne[maxn], idx;
//每个位置的颜色,每种颜色的集合大小,每种颜色映射的哪个颜色编号。
int color[maxn], sz[maxm], p[maxm];
int ans; //现在有多少段颜色。
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
sz[a]++;
}
void merge(int& x, int& y) {
if (x == y) return;
if (sz[x] > sz[y]) swap(x, y);
for (int i = h[x]; i != -1; i = ne[i]) {
int v = e[i];
ans -= (color[v - 1] == y) + (color[v + 1] == y);
}
for (int i = h[x]; i != -1; i = ne[i]) {
int v = e[i];
color[v] = y;
if (ne[i] == -1) {
ne[i] = h[y], h[y] = h[x];
break;
}
}
//把x清空
h[x] = -1;
sz[y] += sz[x], sz[x] = 0;
}
int main() {
scanf("%d%d", &N, &M);
memset(h, -1, sizeof h);
for (int i = 1; i <= N; i++) {
scanf("%d", &color[i]);
if (color[i] != color[i - 1]) ans++;
add(color[i], i);
}
//一开始每种颜色的映射都是自己
for (int i = 0; i < maxm; i++) p[i] = i;
while (M--) {
int op;
scanf("%d", &op);
if (op == 2) printf("%d\n", ans);
else {
int x, y;
scanf("%d%d", &x, &y);
merge(p[x], p[y]);
}
}
return 0;
}
#include
using namespace std;
typedef long long ll;
const int N = 100010, M = 2 * N;
int h[N], e[M], ne[M], idx;
int color[N], cnt[N], son[N], sz[N];
ll ans[N], sum, mx;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs_son(int u, int fa)
{
sz[u] = 1;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
sz[u] += dfs_son(v, u);
if(!son[u] || sz[v] > sz[son[u]]) son[u] = v;
}
return sz[u];
}
void update(int u, int fa, int sign, int pson)
{
int c = color[u];
cnt[c] += sign;
if(cnt[c] > mx) mx = cnt[c], sum = c;
else if(cnt[c] == mx) sum += c;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa || v == pson) continue;
//这里传的参数仍然是pson不是son[v]
//因为你要修改的是除了一个重儿子之外的所有结点,而不是所有重儿子都不修改
update(v, u, sign, pson);
}
}
void dfs(int u, int fa, int op)
{
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa || son[u] == v) continue;
dfs(v, u, 0);
}
if(son[u]) dfs(son[u], u, 1);
update(u, fa, 1, son[u]);
ans[u] = sum;
if(!op) update(u, fa, -1, 0), sum = 0, mx = 0;
}
int main()
{
memset(h, -1, sizeof h);
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &color[i]);
}
for(int i = 1; i < n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
dfs_son(1, -1);
dfs(1, -1, 1);
for(int i = 1; i <= n; i++)
{
printf("%lld ", ans[i]);
}
return 0;
}
#include
#include
#include
using namespace std;
const int maxn = 20000010;
char a[maxn], b[maxn];
int p[maxn], N;
void init() {
int k = 0;
b[k++] = '$', b[k++] = '#';
for (int i = 0; i < N; i++) b[k++] = a[i], b[k++] = '#';
b[k++] = '^';
N = k;
}
void manacher() {
int mr = 0, mid;
for (int i = 1; i < N; i++) {
if (i < mr) p[i] = min(p[2 * mid - i], mr - i);
else p[i] = 1;
while (b[i - p[i]] == b[i + p[i]]) p[i]++;
if (i + p[i] > mr) {
mr = i + p[i];
mid = i;
}
}
}
int main() {
scanf("%s", a);
N = strlen(a);
init();
manacher();
int res = 0;
for (int i = 0; i < N; i++) res = max(res, p[i]);
printf("%lld\n", res - 1);
return 0;
}
题意:输入文件只有两行,每行一个由字符0至9构成的字符串,描述一个项链的表示(保证项链的长度是相等的)。判断是否可能是一条项链。
循环同构
当字符串 S S S 中可以选定一个位置 i i i 满足
S [ i ⋯ n ] + S [ 1 ⋯ i − 1 ] = T S[i\cdots n]+S[1\cdots i-1]=T S[i⋯n]+S[1⋯i−1]=T
则称 S S S 与 T T T 循环同构
字符串 S S S 的最小表示为与 S S S 循环同构的所有字符串中字典序最小的字符串
假如 s [ i : i + k ] = = s [ j : j + k ] s[i:i+k] == s[j:j+k] s[i:i+k]==s[j:j+k],但是 s [ i + k ] > s [ j + k ] s[i+k]>s[j+k] s[i+k]>s[j+k],那么从 i i i ~ i + k i+k i+k 开头的字符串,字典序一定比对应于从 j j j ~ j + k j + k j+k 开头的字符串要小,因此从 i i i 到 i + k i + k i+k 开头的字符串都不是最小的,因此可以把 i i i 直接加上 k + 1 k+1 k+1 然后继续搜索就行.
#include
#include
#include
using namespace std;
const int maxn = 2000010;
char a[maxn], b[maxn];
int n;
int get_min(char s[]) {
int i = 0, j = 1;
while (i < n && j < n) {
int k = 0;
while (k < n && s[i + k] == s[j + k]) k++;
if (k == n) break;
if (s[i + k] > s[k + j]) i += k + 1;
else j += k + 1;
if (i == j) j++;
}
//最小表示开头的那个字母应该是一直不被更新。
int k = min(i, j);
s[k + n] = 0;
return k;
}
int main() {
scanf("%s%s", a, b);
n = strlen(a);
//把原串复制为之前的两倍。
memcpy(a + n, a, n);
memcpy(b + n, b, n);
int x = get_min(a), y = get_min(b);
if (strcmp(a + x, b + y)) printf("No\n");
else{
printf("Yes\n");
printf("%s\n", a + x);
}
return 0;
}