Codeforces Global Round 1~3
回答
个关于 的询问,每次询问求: ,
假设
打麻将,n种牌,每种牌
个,出牌可以出三个一样的 ,也可以出个顺子 。求最多能出多少次牌。
首先可以敲定如果我们出了大于等于三次顺子,可以转化为出了三次三个一样的牌。那么不妨就枚举出顺子的次数,0,1,2三次。注意到当前牌的剩余还跟上一张牌的顺子方案有关,那么还得记录上一张牌的顺子状态。
设
有N堆石头,每堆是
个,每一次操作可以将某个 变成 ,问最终是不是能变成数组
神秘构造题。一次操作相当于交换了差分数组相邻两项...求一下c数组的差分数组、t数组的差分数组,排个序看看是不是一样的就行了
把一棵有根树,根节点是1,按dfs序拍平,每次询问问dfs序在区间
内的叶子到某节点 的最短距离。1不算叶子
题目明示dfs序拍平。考虑节点
所以先求根节点到叶子节点的距离,存在线段树里,然后dfs一遍树,每dfs到一个节点就在线段树上利用区间修改去递推。时间复杂度
顺便测了一下我的线段树模板...
点击查看代码
void dfs(int u, int fa, int &stamp)
{
dfn[u] = ++stamp, size[u] = 1;
for (auto [v, w] : G[u])
if (v != fa)
{
dist[v] = dist[u] + w;
dfs(v, u, stamp);
size[u] += size[v];
}
init[dfn[u]].mn = (size[u] == 1) ? dist[u] : 1e18;
}
void dfs(int u, int fa, SegmentTree &Tree)
{
for (int x : Q[u])
qry[x].ans = Tree.query(qry[x].l, qry[x].r);
for (auto [v, w] : G[u])
if (v != fa)
{
Tag delta[2] = {-w, w};
Tree.apply(dfn[v], dfn[v] + size[v] - 1, delta[0]);
if (dfn[v] - 1 >= 1)
Tree.apply(1, dfn[v] - 1, delta[1]);
if (size[1] >= dfn[v] + size[v])
Tree.apply(dfn[v] + size[v], size[1], delta[1]);
dfs(v, u, Tree);
Tree.apply(dfn[v], dfn[v] + size[v] - 1, delta[1]);
if (dfn[v] - 1 >= 1)
Tree.apply(1, dfn[v] - 1, delta[0]);
if (size[1] >= dfn[v] + size[v])
Tree.apply(dfn[v] + size[v], size[1], delta[0]);
}
}
在树上玩井字棋游戏。白先手,有一些点已经涂成白色了,问是白赢还是黑赢。
首先井字棋先手必胜,后手求和。然后还有一些点已经涂成白色了,黑色真招谁惹谁了...
不如先考虑空树的情况吧。
首先如果有个节点的度数大于3,那么白色必胜。
白色下1号点,黑色必须去堵一边。白色再下另一边,还至少剩下两边,黑色只能堵一边,所以白色必胜。
接下来就可以考虑节点度数为3、为2的情况。
一个度数为3的节点,如果有两个子节点不是叶子节点。那么白色肯定也必胜。
白色先手下1号,接下来无论黑色下在哪,白色长度为2的链都有两个方向可以扩充成长度为3的链
如果所有节点度数为2(除叶子节点),显然是一个平手。
所以重点还是在度数为3、连着两个叶子节点的点。
假设有且仅有两个这样的点。
这种情况下,白色下1号点,黑色只能下在6或者2,那么白色往反方向扩充,就会出现一个有两个方向可扩充的长度为2的链,白色必胜。
而这种情况下无论怎样都不会出现两个方向可扩充的长度为2的链,因此是平手。
所以问题在于是否在度为3的节点处能出现长度为2的链。当白色下在一个点时,黑色要堵一边。两个度数为3的节点
接下来考虑有3个度为3的节点
所以当度为3的节点大于3个的时候,先手必胜。
所以总结一下,有度数大于3的节点,先手必胜;度数等于3的节点,只有一个相邻的叶子节点,先手必胜;度数等于3且有两个相邻的叶子节点的点,如果有大于2个,先手必胜,如果有2个且距离为奇数,先手必胜。剩下的情况都是平局。
那么考虑如果预先放置了白色节点怎么做。可以等价于一个空的树,要在一个节点上放白色,那么黑色必然要放在一个位置去堵他,把这个黑色的点刨去就是我们面临的原树了。要构造这样的case,就要用到度数等于3的那种情况。把这个已经涂成了白色的点置空,再连一个度数为3、连着两个叶子节点的点。白色肯定喜欢下这样的节点,而且黑色必然去堵上这个度数为3的节点,连着的两个叶子,白色也不会去考虑它们,对原树不会造成影响。
这样改造完之后就成为空树的井字棋问题了。实现一下就好了。空间开4倍。
点击查看代码
void Main()
{
int n;
scanf("%d", &n);
for (int i = 1; i < n; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
deg[u]++, deg[v]++;
}
scanf("%s", str + 1);
int m = n;
for (int i = 1; i <= n; ++i)
if (str[i] == 'W')
{
G[i].push_back(m + 1), G[m + 1].push_back(i);
G[m + 1].push_back(m + 2), G[m + 2].push_back(m + 1);
G[m + 1].push_back(m + 3), G[m + 3].push_back(m + 1);
deg[i]++, deg[m + 1] += 3, deg[m + 2]++, deg[m + 3]++;
m += 3;
}
std::function<void()> CLEAR = [m]()
{
for (int i = 1; i <= m; ++i)
G[i].clear(), deg[i] = 0;
};
for (int i = 1; i <= m; ++i)
if (deg[i] >= 4)
return printf("White\n"), CLEAR();
int vert[3] = {0, 0};
for (int i = 1, size = 0; i <= m; ++i)
if (deg[i] >= 3)
{
++size;
if (size < 3)
vert[size] = i;
else
return printf("White\n"), CLEAR();
}
for (int i = 1; i <= 2; ++i)
if (int u = vert[i])
{
int size = 0;
for (int v : G[u])
if (deg[v] >= 2)
size++;
if (size >= 2)
return printf("White\n"), CLEAR();
}
std::function<bool(int, int, int, int)> dfs = [&](int u, int fa, int len, int target) -> bool
{
if (u == target)
return len % 2 == 0;
bool res = false;
for (int v : G[u])
if (v != fa)
res |= dfs(v, u, len + 1, target);
return res;
};
if (vert[1] && vert[2])
if (dfs(vert[1], 0, 0, vert[2]))
return printf("White\n"), CLEAR();
printf("Draw\n"), CLEAR();
}
构造一个长度为
的数字串,使得出现的数字子串的值在 内的子串最多。
感觉上是个AC自动机,把
然后因为状态太多就成为了TLE自动机...
这个TLE自动机里会出现大量的满十叉树。
满十叉树就是所有可能的状态都会出现,那么对于这里面每一个终止节点,它们的权值都是相同的,没有必要去走遍所有节点。当走到一个满十叉树的根的时候,就可以提前算出对于该满十叉树一个基础权值base,后面无论跟什么都会有base的权值,然后跳出这个满十叉树去别的节点里获取更多特殊权值。
那么这些满十叉树都怎么识别呢?就要用到数位DP的思想了。数位DP里面把
接下来考虑dp。
设
最后dfs一个方案就行了。实现时我把
点击查看代码
const int MAXN = 2000, MAXR = 800;
struct state
{
int trans[10], fail;
} acam[MAXR * 2 + 5];
#define trans(u, x) acam[u].trans[x]
#define fail(u) acam[u].fail
int cnt = 0, base[MAXR * 2 + 5][MAXN + 5][10];
void insert(char *s, int val)
{
int u = 0, len = strlen(s);
for (int i = 0; i < len; ++i)
{
int x = s[i] - '0';
for (int j = (u == 0); j < x; ++j)
base[u][len - i][j] += val;
if (i == len - 1)
base[u][len - i][x] += val;
if (!trans(u, x))
trans(u, x) = ++cnt;
u = trans(u, x);
}
}
void build()
{
std::queue<int> q;
for (int i = 0; i < 10; ++i)
if (trans(0, i))
q.push(trans(0, i));
while (!q.empty())
{
int u = q.front();
q.pop();
for (int x = 0; x < 10; ++x)
if (int &v = trans(u, x))
q.push(v), fail(v) = trans(fail(u), x);
else
v = trans(fail(u), x);
}
}
char L[MAXR + 5], R[MAXR + 5];
int n;
bool vis[MAXN + 5][MAXR * 2 + 5];
ll dp[MAXN + 5][MAXR * 2 + 5];
void Main()
{
scanf("%s%s", L + 1, R + 1);
scanf("%d", &n);
int l = strlen(L + 1), r = strlen(R + 1);
for (int i = l; i >= 1; --i)
{
if (L[i] == '0')
L[i] = '9';
else
{
L[i]--;
break;
}
}
if (L[1] == '0')
{
for (int i = 1; i <= l; ++i)
L[i] = L[i + 1];
L[l--] = 0;
}
if (l)
insert(L + 1, -1);
insert(R + 1, 1);
build();
for (int i = std::max(1, l); i < r; ++i)
for (int j = 1; j < 10; ++j)
base[0][i][j]++;
for (int i = 1; i <= cnt; ++i)
for (int j = 1; j <= n; ++j)
for (int k = 0; k <= 9; ++k)
base[i][j][k] += base[fail(i)][j][k];
for (int i = 0; i <= cnt; ++i)
for (int j = 2; j <= n; ++j)
for (int k = 0; k <= 9; ++k)
base[i][j][k] += base[i][j - 1][k];
memset(dp, 0xcf, sizeof(dp));
dp[0][0] = 0;
for (int i = 0; i < n; ++i)
for (int u = 0; u <= cnt; ++u)
{
if (dp[i][u] < 0)
continue;
for (int k = 0; k <= 9; ++k)
{
int v = trans(u, k);
dp[i + 1][v] = std::max(dp[i + 1][v], dp[i][u] + base[u][n - i][k]);
}
}
// for (int i = 0; i <= n; ++i)
// for (int u = 0; u <= cnt; ++u)
// printf("dp[%d][%d] = %lld\n", i, u, dp[i][u]);
ll ans = 0;
for (int u = 0; u <= cnt; ++u)
if (dp[n][u] > ans)
ans = dp[n][u];
printf("%lld\n", ans);
std::vector<int> best;
std::function<bool(int, int)> dfs = [&](int i, int u) -> bool
{
if (i == n && dp[i][u] == ans)
return true;
if (vis[i][u])
return false;
vis[i][u] = true;
for (int k = 0; k <= 9; ++k)
{
int v = trans(u, k);
if (dp[i + 1][v] == dp[i][u] + base[u][n - i][k])
if (dfs(i + 1, v))
return best.push_back(k), true;
}
return false;
};
dfs(0, 0);
for (int i = best.size() - 1; i >= 0; --i)
printf("%d", best[i]);
printf("\n");
}
GlobalRound2:什么牛马构造题专场……
未完待续
CF1119C
CF1119D
CF1119E
CF1119F
CF1119G
CF1119H
这题先咕了,不会多项式...
CF1148C
CF1148D
CF1148E
CF1148F
CF1148G
CF1148H
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】