2023杭电多校第一场
手还是有点生(
随便写点东西.jpg
1001
实际上把链找出来之后就可以把树扔了(
然后在某个位置上出现的点它的出现位置就可以表示为k∗dis+b,b是位置
对于S的每个数字,找它在T的位置,能拿到两个同余方程,形如x=t1(modn)和x=t2(modm)找excrt最小正整数解即可。
代码是学弟写的,摸了(
#include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<queue> #define int long long using namespace std; typedef long long LL; const int N = 3010, M = 6010; int n, m; int h[N], e[M], ne[M], idx; int depth[N], fa[N]; vector<int> va, vb; void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; } void dfs(int Now ,int Fa){ fa[Now] = Fa; for (int i = h[Now]; i != -1 ; i = ne[i]){ int v = e[i]; if (v == Fa) continue; depth[v] = depth[Now] + 1; dfs(v,Now); } } vector<int> lca(int st, int ed) { bool flag = true; vector<int> vst, ved, res; if(depth[st] <= depth[ed]) { swap(st, ed); flag = false; } while(depth[st] > depth[ed]) { vst.push_back(st); st = fa[st]; } while(st != ed) { vst.push_back(st); ved.push_back(ed); st = fa[st]; ed = fa[ed]; } vst.push_back(st); while(ved.size()) { vst.push_back(ved.back()); ved.pop_back(); } if(!flag) { while(vst.size()) { res.push_back(vst.back()); vst.pop_back(); } return res; } return vst; } LL exgcd(LL a, LL b, LL &x, LL &y) { if(b == 0) { x = 1, y = 0; return a; } LL d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } signed main() { int T; scanf("%lld", &T); while(T --) { scanf("%lld%lld", &n, &m); memset(h, -1, sizeof h); memset(e, 0, sizeof e); memset(ne, 0, sizeof ne); idx = 0; for(int i = 1; i <= n - 1; i ++) { depth[i] = 0; int a, b; scanf("%lld%lld", &a, &b); add(a, b), add(b, a); } depth[1] = 1; dfs(1,1); while(m --) { LL ans = 1e9; int sa, ta, sb, tb; scanf("%lld%lld%lld%lld", &sa, &ta, &sb, &tb); va.clear(), vb.clear(); vector<int> vaa = lca(sa, ta); vector<int> vbb = lca(sb, tb); int la = vaa.size(); int lb = vbb.size(); for(int i = 1; i < la; i ++) va.push_back(vaa[i]); for(int i = 1; i < lb; i ++) vb.push_back(vbb[i]); for(int i = la - 2; i >= 1; i --) va.push_back(vaa[i]); for(int i = lb - 2; i >= 1; i --) vb.push_back(vbb[i]); va.push_back(vaa[0]); vb.push_back(vbb[0]); la = va.size(); lb = vb.size(); int ans1 = 0; for(int i = 0; i < va.size(); i ++) { int numa = va[i]; for(int j = 0; j < vb.size(); j ++) if(va[i] == vb[j]) { LL numb = vb[j]; LL x = 0; LL a1 = la, a2 = lb; LL m1 = (i + 1)%la, m2 = (j + 1)%lb; LL k1, k2; LL d = exgcd(a1, a2, k1, k2); if((m2 - m1) % d != 0) { continue; } k1 *= (m2 - m1) / d; // 获得特解 LL t = a2 / d; k1 = (k1 % t + t) % t; // 在通解中找到最小的解 x = k1 * a1 + m1; // 目前两个方程合并完后的x的特解 LL a = a1 / d * a2; // a1 a2的最小公倍数 m1 = k1 * a1 + m1; a1 = a; if(x != -1) x = (m1 % a1 + a1) % a1; // 最小正整数解 if (x!= -1 && x < ans){ ans = x; ans1 = numa; } } } if (ans != 1e9) printf("%lld\n", ans1); else printf("-1\n"); } } }
1002
f[i][0/1/2]表示当前点不选且被覆盖/选了/不选且不被覆盖
转移挺好写的
#include <bits/stdc++.h> #define int long long using namespace std; int T; const int inf = 1e9+7; const int mx = 2e5+5; int a[mx]; vector<int> G[mx]; int dp[mx][3]; void dfs(int Now,int fa){ dp[Now][1] = a[Now]; dp[Now][0] = dp[Now][2] = 0; bool flag = false; int chg = inf; for (auto v : G[Now]){ if (v == fa) continue; dfs(v,Now); dp[Now][1] += min(dp[v][0],min(dp[v][1],dp[v][2])); dp[Now][2] += min(dp[v][1],dp[v][0]); if (dp[v][1] > dp[v][0]){ dp[Now][0] += dp[v][0]; chg = min(chg,dp[v][1]-dp[v][0]); }else{ flag = true; dp[Now][0] += dp[v][1]; } } if (!flag) dp[Now][0] += chg; } signed main(){ scanf("%lld",&T); while (T--){ int N; scanf("%lld",&N); for (int i = 1 ; i <= N ; i ++){ scanf("%lld",&a[i]); G[i].clear(); dp[i][0] = dp[i][1] = dp[i][2] = 0; } for (int i = 1 ; i < N ; i ++){ int x,y; scanf("%lld%lld",&x,&y); G[x].push_back(y); G[y].push_back(x); } dfs(1,1); printf("%lld\n",min(dp[1][1],dp[1][0])); } return 0; }
1005
把串二倍延长一下,实际上枚举M个位置就可以把所有的可能串给枚举上
字符串哈希一下,存一下最小的hash值,最后直接比较即可。
*998244353被卡了,差评
#include<bits/stdc++.h> using namespace std; const int mx = 2e5+10; const unsigned long long P = 429496729; int N,M; int T; unsigned long long tt[mx]; unsigned long long p[mx]; unsigned long long pref[mx]; unsigned long long GetHash(int l, int r) { return pref[r] - pref[l - 1] * p[r - l + 1]; } unsigned long long Hash[mx]; int main(){ p[0] = 1; for (int i = 1 ; i <= mx - 10 ; i ++) p[i] = p[i-1] * P; cin >> T; while (T--){ scanf("%d%d",&N,&M); for (int i = 1 ; i <= N ; i ++){ Hash[i] = 0; } for (int i = 1 ; i <= N ; i ++){ string ss; cin >> ss; ss = ss + ss; ss = ' ' + ss; int Len = ss.length(); pref[0] = 0; for (int j = 1 ; j <= Len ; j ++){ pref[j] = pref[j-1] * P + ss[j]; } Hash[i] = pref[M]; unsigned long long mn = Hash[i]; for (int j = 1 ; j <= M ; j ++){ unsigned long long x = GetHash(j,j+M-1); mn = min(mn,x); } tt[i] = mn; } int Q; cin >> Q; while (Q--){ int x,y; scanf("%d%d",&x,&y); if (tt[x] == tt[y]) printf("Yes"); else printf("No"); if (T!=0 || Q != 0) printf("\n"); } } }
1008
签到,跳过.jpg
1012
根据树删边博弈,SG[x] = (SG[v]+1)的xor和
v是x的所有儿子
SG[root]如果是0先手赢
然后暴力换个根就好了,看有多少点的SG不是0
#include <bits/stdc++.h> #define ll long long using namespace std; int cnt = 0; int read(){ int f=1,k=0; char c=getchar(); while(c<'0'||c>'9'){ c=getchar(); } while(c>='0'&&c<='9'){ k=k*10+c-'0'; c=getchar(); } return f*k; } const int mx = 2e5+10; const int MOD =1e9+7; vector<int> G[mx]; int T; int N; ll SG[mx]; int Fa[mx]; ll Pow(int x,int y){ ll ans = 1; for (;y;y>>=1){ if (y&1) ans = (ans * 1ll * x) %MOD; x = 1ll*x * x %MOD; } return ans; } void dfs(int Now,int fa){ Fa[Now] = fa; SG[Now] = 0; for (auto v:G[Now]){ if (v == fa) continue; dfs(v,Now); SG[Now] ^= (SG[v]+1); } } void dfs1(int Now,int fa){ if (Now != 1){ SG[fa] ^= (SG[Now]+1); SG[Now] = SG[Now] ^ (SG[fa] + 1); } if (SG[Now] != 0) cnt ++; for (auto v : G[Now]){ if (v == fa) continue; dfs1(v,Now); } if (Now != 1){ SG[Now] ^= (SG[fa]+1); SG[fa]^=(SG[Now] + 1); } } signed main(){ T=read(); while (T--){ N = read(); for (int i = 1 ; i <= N ; i ++){ Fa[i] = 0; SG[i] = 0; G[i].clear(); } for (int i = 1 ; i < N ; i ++){ int x,y; x=read(),y=read(); G[x].push_back(y); G[y].push_back(x); } dfs(1,1); cnt = 0; dfs1(1,1); ll inv = Pow(N,MOD-2); printf("%lld\n",1ll*inv*cnt%MOD); } return 0; }
1003
简单的区间dp,可惜赛中没看,在写1010(
根据套路可以考虑f[l][r]表示l到r全消干净的最大贡献
但是这些信息不够转移
所以多补两维
f[l][r][k][l]表示l,r之间剩一张k类型,等级为m的牌
f[l][r][0][0]表示消完
那么转移显然就是
f[l][r][k][m]=max(f[l][mid][k][m−1],f[mid+1][r][k][l−1],f[l][mid][k][m]+f[mid+1][r][0][0])
f[l][r][0][0]=max(f[l][mid][0][0]+f[mid+1][r][0][0],f[l][r][k][m]+val(k,m))
for一下结束(
#include <bits/stdc++.h> #define ll long long const int mx = 105; using namespace std; ll a[mx],b[mx],v[mx]; ll Level[11]; ll f[mx][mx][22][11]; const ll INF = 0x3f3f3f3f3f3f3f3f; int main(){ int T; cin >> T; while (T--){ int N,M,lim,p; cin >> N >> M >> lim >> p; lim = min(7,lim); Level[1] = 1; for (int i = 2 ; i <= lim ; i ++) Level[i] = Level[i-1] * p; for(int i = 1 ; i <= N ; i ++) cin >> a[i]; for (int i = 1 ; i <= M ; i ++) cin >> v[i]; memset(f,-0x3f,sizeof(f)); for (int i = 1 ; i <= N ; i ++){ f[i][i][0][0] = v[a[i]]; f[i][i][a[i]][1] = 0; } for (int l = N ; l >= 1 ; l --) for (int r = l + 1 ; r <= N ; r++) for(int mid = l ; mid < r ; mid ++){ f[l][r][0][0] = max(f[l][r][0][0],f[l][mid][0][0]+f[mid+1][r][0][0]); for (int j = 1 ; j <= M ; j ++){ f[l][r][j][1] = max(f[l][r][j][1],f[l][mid][j][1]+f[mid+1][r][0][0]); for (int k = 2 ; k <= lim ;k ++){ f[l][r][j][k] = max(f[l][r][j][k],f[l][mid][j][k-1]+f[mid+1][r][j][k-1]); f[l][r][0][0] = max(f[l][r][0][0],f[l][r][j][k] +v[j]*Level[k]); } } } cout << f[1][N][0][0] << '\n'; } return 0; }
干啥啥不行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步