CF diary II
每 题一篇 。
1717E. Madoka and The Best University
题意
求 。
题解
考虑枚举 ,设 ,于是需要满足 ,对于每个 再枚举 ,此时对于一组 ,所有满足该条件的三元组对应的值为 。接下来考虑如何计算有多少对满足该条件的 ,考虑 ,那么满足条件的 的数量显然为 。于是对于枚举的每一组 ,其对答案的贡献为 。直接计算即可,复杂度 。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;
LL N, v[maxn], prime[maxn], phi[maxn];
LL phis(LL n)
{
phi[0] = 0, phi[1] = 1;
LL m = 0;
for (LL i = 2; i <= n; i++)
{
if (!v[i])
{
v[i] = i;
prime[++m] = i;
phi[i] = i - 1;
}
for (LL j = 1; j <= m; j++)
{
if (prime[j] > v[i] || prime[j] > n / i)
break;
v[prime[j] * i] = prime[j];
if (i % prime[j] == 0)
phi[i * prime[j]] = phi[i] * prime[j];
else
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
return m;
}
LL gcd(LL a, LL b)
{
if (!b)
return a;
return gcd(b, a % b);
}
LL lcm(LL a, LL b)
{
return a / gcd(a, b) * b;
}
void solve()
{
phis(N);
LL ans = 0;
for (LL i = 1; i <= N; i++)
{
for (LL j = 2; j * i <= N; j++)
ans = (ans + lcm(N - j * i, i) * phi[j] % MOD) % MOD;
}
cout << ans << endl;
}
int main()
{
IOS, cin >> N, solve();
return 0;
}
1336C. Kaavi and Magic Spell
题意
一个串 ,不断删去其第一个字符,加入新的串 , 一开始为空串,加入时可以选择两种操作,从最左侧加入或者从最右侧加入(空串时也算作两种不同的操作),考虑有多少种不同的操作序列(可以操作任意步),使得 的前缀是另外一个串 。
题解
考虑区间 , 记 为当前串恰好为 的方案数,如果 的长度小于 ,可以视作 后面有若干通配符,之后转移非常显然,结果为 。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 3010;
string S, T;
LL N, M, f[maxn][maxn];
void solve()
{
S = ' ' + S, T = ' ' + T;
for (LL i = 1; i <= N; i++)
{
if (i > M || S[1] == T[i])
f[i][i] = 2;
}
for (LL i = 2; i <= N; i++)
{
for (LL l = 1; l + i - 1 <= N; l++)
{
LL r = l + i - 1;
if (r > M || S[i] == T[r])
f[l][r] = (f[l][r] + f[l][r - 1]) % mod;
if (l > M || S[i] == T[l])
f[l][r] = (f[l][r] + f[l + 1][r]) % mod;
}
}
LL ans = 0;
for (LL i = M; i <= N; i++)
ans = (ans + f[1][i]) % mod;
cout << ans << endl;
}
int main()
{
IOS;
cin >> S >> T;
N = S.length(), M = T.length();
solve();
return 0;
}
1479C. Continuous City
题意
最多使用 个节点,构造一张 ,不能有重边,每条边必须为编号小的节点指向大的节点,各边权值 ,使得 到 恰好有 条路径,其长度分别为 。
题解
考虑这样一个构造方法,我们固定使用 个节点,然后对于每个 ,我们向 连一条权值为 的边直到 。这样的话,从每个 为起点到 的路径恰为长度为 路径各一条,特殊的,从 开始则为 ,于是我们可以将 转化为以 开始,之后对右端点每个二进制位进行考虑,从 连边即可。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 19650827;
const int maxn = 510;
int L, R;
struct Edge {
int from, to, cost;
}E[maxn];
int nums[30];
void solve()
{
cout << "YES" << endl;
int v = 23, e = 0;
for (int i = 2; i <= 22; i++)
{
e++;
E[e] = Edge{ i,23,1 };
}
for (int i = 0; i < 20; i++)
{
for (int j = 1; j <= i + 1; j++)
{
e++;
E[e] = Edge{ 21 - i,21 - i + j,1 << (i - j + 1) };
}
}
int x = L;
int tmp = x;
R -= L;
E[++e] = Edge{ 1,23,tmp };
for (int i = 19; i >= 0; i--)
{
if ((R >> i) & 1)
{
e++;
E[e] = Edge{ 1,22 - i,tmp };
tmp += 1 << i;
}
}
cout << v << ' ' << e << endl;
for (int i = 1; i <= e; i++)
cout << E[i].from << ' ' << E[i].to << ' ' << E[i].cost << endl;
}
int main()
{
IOS;
cin >> L >> R;
solve();
return 0;
}
1368E. Ski Accidents
题意
一张 个顶点的 (可能有重边,每个节点出度 )。现在标记不超过 个顶点,被标记顶点关联的边将被删除,给出一个标记方案,使得最终的图中任意路径长度 。
题解
神仙构造题,反正我是肯定想不出来。。。
考虑将图中的节点划分为三个集合 ,并且满足 ,然后我们标记 中的所有点,这样点数一定不会超出限制,接下来考虑具体的划分方案,我们将入边为空或者仅来自 中的点的节点划入集合 。将入边仅来自 中的点或来自 的点的节点划入集合 ,其他节点划入集合 ,显然该划分合法且由出度 的性质容易证明三个集合满足最初的性质,我们删除 后,由于 集合没有来自 集合的边, 集合内部互相没有连边,因此路径长度都小于 ,如此构造即可。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 200010;
int T, N, M, in[maxn], col[maxn], s[maxn];
vector<int>G[maxn], C;
void add_edge(int from, int to)
{
G[from].push_back(to);
}
void solve()
{
queue<int>que;
for (int i = 1; i <= N; i++)
{
if (!in[i])
que.push(i);
}
while (!que.empty())
{
int v = que.front(); que.pop();
if (col[v] == 0 || col[v] == 4)
s[v] = 1;
else if (col[v] == 1 || col[v] == 5)
s[v] = 2;
else
C.push_back(v), s[v] = 3;
for (auto& to : G[v])
{
col[to] |= (1 << (s[v] - 1)), in[to]--;
if (!in[to])
que.push(to);
}
}
cout << C.size() << endl;
for (auto& v : C)
cout << v << ' ';
cout << endl;
}
int main()
{
IOS;
cin >> T;
while (T--)
{
cin >> N >> M;
C.clear();
for (int i = 1; i <= N; i++)
G[i].clear(), in[i] = s[i] = col[i] = 0;
int u, v;
for (int i = 1; i <= M; i++)
cin >> u >> v, add_edge(u, v), in[v]++;
solve();
}
return 0;
}
1158C. Permutation recovery
题意
一个长为 丢失了的排列 ,现在你知道每个位置右边最近的大于它的元素位置 ,特别的,如果不存在这样的位置, 。但是 数组也有一部分缺失了,它们的值记为 ,找出一个合法的排列或者输出无解。
题解
考虑无解的情况,当且仅当存在 ,使得 时会无解,其他情况只要让 的位置都设成 即可保证不出现无解情况,接下来考虑如何构造一组解,我们考虑第一个 的位置,显然其左侧以及右侧的数字均不超过它,于是它就是整个排列的最大值,我们考虑 递归地求解,记 为求解区间 ,元素为 时的情况,根据上面的分析,我们显然可以把 递归求解,安排尽可能大的值,然后对于 这一部分,依然发现这一部分第一个 的位置为当前段的最大值,于是预处理出每个 的 即可将 分割为若干段,字段的求解类似,将 对应替换为 即可。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 500010;
int T, N, nxt[maxn], A[maxn], dat[maxn];
vector<int>G[maxn];
void add(int i, int x)
{
while (i <= N + 1)
dat[i] += x, i += i & (-i);
}
int sum(int i)
{
int ret = 0;
while (i)
ret += dat[i], i -= i & (-i);
return ret;
}
void dfs(int l, int r, int x)
{
if (l > r)
return;
if (l == r)
{
A[l] = x;
return;
}
int lst = l - 1;
for (auto& i : G[r + 1])
A[i] = x, dfs(lst + 1, i - 1, x - 1), x -= i - lst, lst = i;
}
void solve()
{
for (int i = 1; i <= N; i++)
{
if (nxt[i] == -1)
nxt[i] = i + 1;
else
{
if (sum(nxt[i] - 1) - sum(i))
{
cout << -1 << endl;
return;
}
add(nxt[i], 1);
}
G[nxt[i]].push_back(i);
}
dfs(1, N, N);
for (int i = 1; i <= N; i++)
cout << A[i] << ' ';
cout << endl;
}
int main()
{
IOS;
cin >> T;
while (T--)
{
cin >> N;
for (int i = 1; i <= N; i++)
cin >> nxt[i], dat[i] = 0, G[i].clear();
dat[N + 1] = 0, G[N + 1].clear();
solve();
}
return 0;
}
1036F. Relatively Prime Powers
题意
次询问,求 中有多少数,其各个质因数的指数的 为 。
题解
看到 相关计数可以考虑莫比乌斯反演,我们记 为各质因数指数 为 的数字的数量, 为各质因数指数 为 的倍数数字的数量,显然 ,根据莫反的形式二,我们有:
即为所求,考虑如何计算 ,考虑如果一个数各质因数指数的 为 的倍数,一定可以写成 的形式,也就是说,求出最大的满足 的 即可,即 ,注意 是为了不用将 算进去,然后直接开根卡精度, 只要枚举到 即可,开根适当大一点最后再快速幂 往回减。
代码
view code
TLE
1401F. Reverse and Swap
题意
种操作:
每 个元素作为字段,将所有子段分别翻转
每 个元素作为字段,相邻两个字段互换。
输出 。
个元素, 次操作。
题解
注意到各划分的子段一定是一棵线段树中的某一节点,之后就比较好处理, , 操作不用多说, 操作需要将 层以下各节点都打上翻转标记, 操作需要将 层各节点都打上翻转标记。一开始考虑怎么打这些标记很久,后来发现所有的标记都是整层地打,于是我们只需要为每一层记录标记,然后也不用下传标记实质性地改动树的结构,查询/修改时如果当前节点所在层被打上标记,那么将左子树视为右子树,右子树视为左子树即可,复杂度 。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mk make_pair
//#define int LL
//#define lc tr[p].ch[0]
//#define rc tr[p].ch[1]
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 300010;
LL N, Q, A[maxn];
struct Node {
int ch[2], k;
LL sum;
}tr[maxn * 4];
int rev[20];
void pushup(int p)
{
int lc = tr[p].ch[0], rc = tr[p].ch[1];
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
int tot = 0, rk = 0;
int build(int x)
{
int p = ++tot;
tr[p].k = x;
if (x)
tr[p].ch[0] = build(x - 1), tr[p].ch[1] = build(x - 1), pushup(p);
else
tr[p].sum = A[++rk];
return p;
}
void modify(int p, int x, LL val)
{
if (!tr[p].k)
{
tr[p].sum = val, A[x] = val;
return;
}
int lc = tr[p].ch[0], rc = tr[p].ch[1];
if (rev[tr[p].k])
swap(lc, rc);
int mid = 1 << (tr[p].k - 1);
if (x <= mid)
modify(lc, x, val);
else
modify(rc, x - mid, val);
pushup(p);
}
LL query(int p, int l, int r)
{
if (l == 1 && ((1 << (tr[p].k)) == r) || tr[p].k == 0)
return tr[p].sum;
int mid = 1 << (tr[p].k - 1), lc = tr[p].ch[0], rc = tr[p].ch[1];
if (rev[tr[p].k])
swap(lc, rc);
if (r <= mid)
return query(lc, l, r);
else if (l > mid)
return query(rc, l - mid, r - mid);
else
return query(lc, l, mid) + query(rc, 1, r - mid);
}
void solve()
{
build(N);
int op, x, k, l, r;
while (Q--)
{
cin >> op;
if (op == 1)
cin >> x >> k, modify(1, x, k);
else if (op == 3)
cin >> k, rev[k + 1] ^= 1;
else if (op == 2)
{
cin >> k;
for (int i = k; i; i--)
rev[i] ^= 1;
}
else
cin >> l >> r, cout << query(1, l, r) << endl;
}
}
int main()
{
IOS;
cin >> N >> Q;
for (int i = 1; i <= (1 << N); i++)
cin >> A[i];
solve();
return 0;
}
1041F. Ray in the tube
题意
两条平行于 轴的直线,直线上有若干带命中的整点,现在从下面直线选择任意一个整点(可以不是带命中的点),射出激光,击中直线后会反弹,并且反弹点必须为整点,求激光可以命中的最大待命中整点数。
题解
不太好想的思维题,考虑激光反弹一次后是一个三角形,底边为 。可以发现,如果 有任意大于 的奇约数 ,则底边长为 的激光可以经过的点是之前激光的超集。于是选择的 必须没有大于 的奇约数,也就是 。有了这个结论后就十分显然了,枚举 ,考虑起始点位于各 模数处的最大值即可,用 维护,复杂度 。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair < int, int > PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 100010;
const int maxm = 65;
int N, X, M, Y, A[maxn], B[maxn];
map<int, int>mp;
void solve()
{
int ans = 0;
for (int i = 1; i <= N; i++)
mp[A[i]]++;
for (int i = 1; i <= M; i++)
mp[B[i]]++;
for (auto& [x, y] : mp)
ans = max(ans, y);
for (int j = 0; j < 30; j++)
{
mp.clear();
for (int i = 1; i <= N; i++)
mp[A[i] % (1 << j + 1)]++;
for (int i = 1; i <= M; i++)
mp[(B[i] + (1 << j)) % (1 << j + 1)]++;
for (auto& [x, y] : mp)
ans = max(ans, y);
}
cout << ans << endl;
}
int main()
{
IOS;
cin >> N >> X;
for (int i = 1; i <= N; i++)
cin >> A[i];
cin >> M >> Y;
for (int i = 1; i <= M; i++)
cin >> B[i];
solve();
return 0;
}
1481E. Sorting Books
题意
本书,每次操作可以选择任意一本书放到书架的末尾,每本书有颜色 。当所有颜色的书都被放在一起时,停止操作,求最少操作数。
题解
显然每本书至多被挪动一次,我们考虑反向来求解整个书架最多有多少书不需要移动,记 为从 开始的后缀中最多有多少书不被移动。考虑到第 个位置时,如果其需要移动}$ ,如果其不动,那么有两种情况,首先如果其是其颜色的左端点,那么如果它不动并且最后还要满足要求,那么就应当让从左端点到右端点区间内所有其他颜色移动,于是 。 记录的是当前一个后缀的信息,如果不是左端点,那么如果要让其颜色在一起,就得让在它前面的颜色相同的都放到后面再把中间颜色不同的挪走,也就是后缀中所有颜色不同的都得挪,于是 ,最后所有转移取 即可。
最后答案为 。
后两种转移也可以理解为尝试 内所有颜色为 的书不动时的结果。(因为自己不动,其后面的就不能动)。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair < int, int > PII;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 500010;
const int maxm = 65;
int N, A[maxn], f[maxn], l[maxn], r[maxn], cnt[maxn];
bool vis[maxn];
void solve()
{
for (int i = 1; i <= N; i++)
{
if (!vis[A[i]])
vis[A[i]] = true, l[A[i]] = i;
}
mst(vis, 0);
for (int i = N; i >= 1; i--)
{
if (!vis[A[i]])
vis[A[i]] = true, r[A[i]] = i;
}
for (int i = N; i >= 1; i--)
{
cnt[A[i]]++;
if (i == l[A[i]])
f[i] = max(f[i], cnt[A[i]] + f[r[A[i]] + 1]);
else
f[i] = max(f[i], cnt[A[i]]);
f[i] = max(f[i], f[i + 1]);
}
cout << N - f[1] << endl;
}
int main()
{
IOS;
cin >> N;
for (int i = 1; i <= N; i++)
cin >> A[i];
solve();
return 0;
}
1034B. Little C Loves 3 II
题意
的棋盘,每次放置一对棋子,两者曼哈顿距离恰好为 ,最多能放多少棋子。
题解
分类讨论找规律思维题。
不妨设 。
显然当 时放不了,接下来考虑 ,这也很好判断,顺着放即可,找出规律即可计算。
之后考虑 ,发现 , 有两个空位, , , 可以放满,其他的 也都可以由 生成,所以必然放满。
接下来考虑 ,我们发现 时有仅空出中间的放 个的方案,我们用这一特点,在 后不断添加 ,使得 为奇数时一定只有一个放不满,我们在 方向亦可以扩展,先添加 ,之后不断向 方向添加 ,亦仅有一个放不满,于是有当 都为奇数时,答案为 。
当 都为偶数时,可以全部由 扩展填满,答案为 。
当 一奇一偶时,与都为奇数类似构造,区别是最后仅放置一个空列,发现也可以填满,于是答案也为 。
代码
view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair < int, int > PII;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 500010;
const int maxm = 65;
LL N, M;
void solve()
{
if (N + M <= 4)
{
cout << 0 << endl;
return;
}
if (N > M)
swap(N, M);
if (N == 1)
{
LL t = max(0LL, M % 6 - 3);
cout << (M / 6 * 6 + t * 2) << endl;
return;
}
else if (N == 2)
{
if (M == 3)
cout << 4 << endl;
else if (M == 7)
cout << 12 << endl;
else
cout << M * 2 << endl;
}
else
{
if (N % 2 && M % 2)
cout << N * M - 1 << endl;
else
cout << N * M << endl;
}
}
int main()
{
IOS;
cin >> N >> M;
solve();
return 0;
}
本文作者:Prgl
本文链接:https://www.cnblogs.com/Prgl/p/16651626.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步