当前位置: 首页 > news >正文

The 2021 ICPC Asia Shenyang Regional Contest (EFBJHLI)题解

The 2021 ICPC Asia Shenyang Regional Contest(EFBJHLI)

2025.8.24

单挑,5题,Cu

E. Edward Gaming, the Champion

签到。

F. Encoded Strings I

签到,模拟。

但是我糖丸了,做了很久,还wa了一发。

B. Bitwise Exclusive-OR Sequence

根据样例2,发现只有01会很好找到无解的情况,而注意到每位是独立的,于是考虑按位做。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e5 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;int fa[N];
int find(int x) {if (x==fa[x]) return x;return fa[x]=find(fa[x]);
}
void merge(int x,int y) {int fx=find(x),fy=find(y);if (fx==fy) return ;fa[fx]=fy;
}void solve() {int n,m;cin>>n>>m;vector<vector<array<int,2>>> G(n+1);for (int i=1;i<=n;++i) fa[i]=i;for (int i=1;i<=m;++i) {int u,v,w;cin>>u>>v>>w;G[u].push_back({v,w});G[v].push_back({u,w});merge(u,v);}bool ok=true;vector<bool> vis(n+1),a(n+1);int cnt1=0,cnt0=0,w;function<void(int,int)> dfs=[&](int x,int val) {if (!ok) return ;a[x]=val;vis[x]=1;if (val==0) cnt0++;else cnt1++;for (auto [y,z]:G[x]) {int nv=(z>>w)&1;if (vis[y]) {if ((val^nv)!=a[y]) ok=false;}else {dfs(y,val^nv);}}};i64 ans=0;for (w=0;w<30;++w) {for (int i=1;i<=n;++i) vis[i]=false;for (int i=1;i<=n;++i) {if (i!=find(i)) continue;cnt1=0,cnt0=0;dfs(i,0);if (ok==false) {cout<<"-1\n";return ;}else {ans+=(1ll<<w)*min<i64>(cnt1,cnt0);}}}cout<<ans<<"\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGEfreopen("C:\\ACM\\code\\test.in", "r", stdin);// freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endifint Case = 1;// cin >> Case;while (Case--) solve();return 0;
}

J. Luggage Lock

~铜牌题

容易想到根据差值处理,发现状态只有\(10000\),直接暴力预处理即可。

又糖了,导致后面没时间写L了。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e6 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;map<array<int,4>,int> f;void init() {array<int,4> now={0,0,0,0};f[now]=0;queue<array<int,4>> q;q.push((now));while (q.size()) {auto ar=q.front();q.pop();for (int l=0;l<4;++l) for (int r=l;r<4;++r) {auto ar0=ar,ar1=ar;for (int i=l;i<=r;++i) ar0[i]=(ar0[i]+1)%10;for (int i=l;i<=r;++i) ar1[i]=(ar1[i]+9)%10;if (!f.count(ar0)) q.push(ar0),f[ar0]=f[ar]+1;if (!f.count(ar1)) q.push(ar1),f[ar1]=f[ar]+1;}}
}void solve() {string a,b;cin>>a>>b;array<int,4> ar;for (int i=0;i<4;++i) {int x=b[i]-a[i];if (x<0) x+=10;ar[i]=x;}cout<<f[ar]<<"\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGEfreopen("C:\\ACM\\code\\test.in", "r", stdin);// freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endifint Case = 1;init();cin >> Case;while (Case--) solve();return 0;
}

H. Line Graph Matching

铜~银牌题,图论。

容易发现就是每次可以获得同一个点的两条边的权值和,然后去掉,问最大权值和。

一开始以为贪心,写出来发现有问题,然后看了看样例,猜到一个结论就是一个联通块的边数是偶数的话,一定可以获得全部边。奇数就要去掉一些边(假设一定是去掉一条边),正着不好做。我们考虑按照边权递减的顺序连边。记录每个连通块边数,和去掉了的权值,用并查集维护。有以下情况。

  • 联通块内部加边,如果原来是奇数,那么加上后,去掉的权值为0,否则为这条边的权值。
  • 联通两个不同奇偶性的块,这个时候合并后为偶数,为0。
  • 偶数和偶数,为这条边的权值。
  • 奇数和奇数,首先若原来的奇数块去掉边没有分开,那么权值就为原来的值,若分成了两个块,根据归纳假设,由于只去掉一条边,那么两个块一定为偶数,因此还是可以删除这一条边。

这里补充上面那个性质的证明,即无向联通图,相邻边两两匹配,若边为偶数,则一定有完美匹配。

找出所有的奇度数点,连向一个新的节点,根据握手引理,点数量为偶数,此时新图所有点度数为偶数,那么存在一条欧拉回路,对于这条路径,将进入某个点和离开某个点的边配对即可。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e5 + 9;
const int iinf = 1e9;
const i64 linf = 1e15;int fa[N],e[N],los[N];
int find(int x) {if (x==fa[x]) return x;return fa[x]=find(fa[x]);
}void solve() {int n,m;cin>>n>>m;vector<array<int,3>> E;i64 ans=0;for (int i=1;i<=m;++i) {int u,v,w;cin>>u>>v>>w;ans+=w;E.push_back({w,u,v});}sort(E.begin(),E.end(),greater<array<int,3>>());for (int i=1;i<=n;++i) fa[i]=i,e[i]=0,los[i]=0;for (auto [w,x,y]:E) {int fx=find(x),fy=find(y);if (fx==fy) {if (e[fx]&1) {los[fx]=0;}else {los[fx]=w;}e[fx]++;}else {if ((e[fx]&1)^(e[fy]&1)) {los[fy]=0;}else {if (e[fx]&1) {los[fy]=min(los[fx],los[fy]);}else {los[fy]=w;}}fa[fx]=fy;e[fy]+=1+e[fx];}}for (int i=1;i<=n;++i) {if (i==find(i))ans-=los[i];}cout<<ans<<"\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGEfreopen("C:\\ACM\\code\\test.in", "r", stdin);// freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endifint Case = 1;// cin >> Case;while (Case--) solve();return 0;
}

比赛结束后发现,图是联通的,那就只需要找到奇数边时删哪条边即可。显然可以枚举边,如果该边是割边,就要判断两边联通块边数:若不是偶数,就跳过,否则继续判断。可以用tarjan找桥,边数可根据dfs处理出度数和计算。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e5 + 9;
const int iinf = 1e9;
const i64 linf = 1e15;struct Q {int v, w,in;
};
multiset<int> st;
vector<vector<int>> ans;
vector<int> cur;
vector<Q> e[N];
int dfn[N], low[N], ed[N],sz[N];
int id;
void tarjan(int u, int fin) {dfn[u]=low[u]=++id;for (auto [v,w,in]:e[u]) {if (in!=fin) {if (!dfn[v]) {tarjan(v,in);sz[u]+=sz[v];low[u]=min(low[u],low[v]);if (low[v]>dfn[u] and ((sz[v]-1)/2)&1) st.erase(w);}else{low[u]=min(low[u],dfn[v]);}}}
}void solve() {int n, m;cin >> n >> m;i64 ans = 0;id=0;for (int i = 1; i <= m; ++i) {int u, v, w;cin >> u >> v >> w;e[u].push_back({v,w,i});e[v].push_back({u,w,i});sz[u]++,sz[v]++;st.insert(w);ans+=w;}if (m&1) tarjan(1,0),ans-=*st.begin();cout<<ans<<"\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGEfreopen("C:\\ACM\\code\\test.in", "r", stdin);// freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endifint Case = 1;// cin >> Case;while (Case--) solve();return 0;
}

L. Perfect Matchings

银牌题。dp

赛时没看到会形成一颗树。最眼瞎的一集

用集合去容斥时间复杂度过高,但发现只用考虑两类边,因此只用考虑树上使用边(点)的数量。

一个显然的思路是维护边不含交点,对禁止的边进行容斥,因为边全在一颗树上,那么我们就很方便进行dp。

而去掉选定的点后,还是是一个完全图,\(2n\)个点完全图的完美匹配数是

\[\frac{\binom{2n}{n} n!}{2^n}=\prod_{i=1}^{n} 2i-1 \]

然后就是计算一颗树上,选出k条边的方案数。

\(f_{i,j,0/1}\) 表示以\(i\)为根,选出来\(j\)条边,根节点是否被选的方案数量,然后就是树上背包问题。

void dfs(int x,int pre) {siz[x]=1;for (auto y:ver[x]) {if (y==pre) continue;dfs(y,x);for (int i=siz[x]/2;i>=0;--i) for (int j=siz[y]/2;j>=0;--j) {if (j) {f[x][i+j][0]=(1ll*f[x][i+j][0]+1ll*f[x][i][0]*(1ll*f[y][j][0]+f[y][j][1])%mod)%mod;f[x][i+j][1]=(1ll*f[x][i+j][1]+1ll*f[x][i][1]*(1ll*f[y][j][0]+f[y][j][1])%mod)%mod;}f[x][i+j+1][1]=(1ll*f[x][i+j+1][1]+1ll*f[x][i][0]*f[y][j][0]%mod)%mod;}siz[x]+=siz[y];}
}

实际上这是\(O(n^2)\)的,不太会算树上背包的复杂度,感觉做树上背包问题多注意调整一下边界,大胆写就行了。

I. Linear Fractional Transformation

金牌题。数学

3个方程,4个未知数,并且是齐次的,因此我们考虑先定一个数。

如果\(c=0\),那么\(f(z)=\frac{a}{d} z+\frac{c}{d}\),再设\(d=1\),然后解出\(a,c\),再验证第3个,若满足,就可以直接带入计算即可。

不满足就设\(c=1\),解出\(a,b,d\)即可。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e6 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;using cd=complex<long double>;
cd ar[10][10];
const double eps = 1e-8;int gauss(int n) {int c, r;for (c = 0, r = 0; c < n; ++c) {int t = r;for (int i = r; i < n; ++i) {if (std::abs(ar[i][c]) > std::abs(ar[t][c])) t = i;}if (std::abs(ar[t][c]) < eps) continue;for (int j = c; j < n + 1; ++j) std::swap(ar[t][j], ar[r][j]);for (int j = n; j >= c; --j) ar[r][j] /= ar[r][c];for (int i = r + 1; i < n; ++i) {if (std::abs(ar[i][c]) > eps) {for (int j = n; j >= c; --j) {ar[i][j] -= ar[r][j] * ar[i][c];}}}r++;}if (r < n) {for (int i = r; i < n; ++i) {if (std::abs(ar[i][n])>eps) return 2;}return 1;}for (int i = n - 1; i >= 0; --i) {for (int j = i + 1; j < n; ++j) {ar[i][n] -= ar[i][j] * ar[j][n];}}return 0;
}bool myequal(cd z1,cd z2) {return std::abs(z1-z2)<eps;
}void solve() {cd z0,a,b,d;array<cd,3> z,w;for (int i=0;i<3;++i) {int x,y;cin>>x>>y;z[i]=cd(x,y);cin>>x>>y;w[i]=cd(x,y);}int x,y;cin>>x>>y;z0=cd(x,y);for (int i=0;i<2;++i) ar[i][0]=z[i],ar[i][0]=z[i],ar[i][1]=1,ar[i][2]=w[i];gauss(2);a=ar[0][2],b=ar[1][2];if (myequal(a*z[2]+b,w[2])) {cd ans=a*z0+b;cout<<fixed<<setprecision(12)<<ans.real()<<" "<<ans.imag()<<"\n";return ;}for (int i=0;i<3;++i) ar[i][0]=z[i],ar[i][0]=z[i],ar[i][1]=1,ar[i][2]=-w[i],ar[i][3]=w[i]*z[i];gauss(3);a=ar[0][3],b=ar[1][3],d=ar[2][3];cd ans=(a*z0+b)/(z0+d);cout<<fixed<<setprecision(12)<<ans.real()<<" "<<ans.imag()<<"\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGEfreopen("C:\\ACM\\code\\test.in", "r", stdin);// freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endifint Case = 1;cin >> Case;while (Case--) solve();return 0;
}

或者利用线性分式变换的保交比性

分式线性变换

\[f(z)=\frac{az+b}{cz+d} (ad-bc \neq 0) \]

交比是四个复数 z, z1, z2, z3 的一种组合,定义为:

\[(z,z_1,z_2,z_3)=\frac{(z-z_1)(z_2-z_3)}{(z-z_3)(z_2-z_1)} \]

\[(z,z_1,z_2,z_3)=(f(z),f(z_1),f(z_2),f(z_3)) \]

从而

\[w_0=\frac{\omega_3(z_0-z_1)(z_2-z_3)(\omega_2-\omega_1)-\omega_1(z_0-z_3)(z_2-z_1)(\omega_2-\omega_3)}{(z_0-z_1)(z_2-z_3)(\omega_2-\omega_1)-(z_0-z_3)(z_2-z_1)(\omega_2-\omega_3)} \]

void solve() {cd z0,a,b,d;array<cd,4> z,w;for (int i=1;i<=3;++i) {int x,y;cin>>x>>y;z[i]=cd(x,y);cin>>x>>y;w[i]=cd(x,y);}int x,y;cin>>x>>y;z[0]=cd(x,y);cd A=(z[0]-z[1])*(z[2]-z[3])*(w[2]-w[1]),B=(z[0]-z[3])*(z[2]-z[1])*(w[2]-w[3]);cd ans=(w[3]*A-w[1]*B)/(A-B);cout<<fixed<<setprecision(12)<<ans.real()<<" "<<ans.imag()<<"\n";
}
http://www.sczhlp.com/news/39805/

相关文章:

  • 抽象层破绽:Behringer Wing混音器与DigiMixer的技术适配挑战
  • Docker Compose 使用指南 - 1Panel 版
  • 1. LangChain4J 理论概述 - Rainbow
  • 百度提交网站入口网站免费自助建站
  • 企业网站源码带后台管理搜索引擎推广步骤
  • 网站系统怎么做关键词搜索引擎工具
  • 河间做网站的公司app推广代理去哪里找
  • 免费视频外链生成推荐百家号关键词seo优化
  • 那里做网站深圳专业建站公司
  • 网站 建设 毕业设计 要求成人零基础学电脑培训班
  • html5 手机网站开发网络培训学校
  • 微网站怎么开通百度地图导航2021最新版
  • 解决Android多个相同动态链接库.so文件的冲突
  • megacc构建进化树xxx.mao文件的生成
  • AI 赋能 “低空 +”,开启城乡治理新纪元
  • UE5 plugin OpenCV 踩坑:图片无故发蓝且不是RGB2BGR的问题怎么回事
  • 【IEEE出版】第七届 IEEE 能源、电力与电网国际学术会议(IEEE-ICEPG 2025)
  • 四川建设网电话号码是多少seo引擎优化方案
  • 任何用c语言做网站东莞百度推广排名优化
  • 网站开发e r图免费的自媒体一键发布平台
  • php网站开发更换模板seo搜索引擎专员
  • 使用 1Panel PHP 运行环境部署 WordPress
  • HuggingFace课程-2. 使用 Transformers 模型
  • P9036 「KDOI-04」挑战 NPC Ⅲ 题解
  • 网站密钥怎么做百度竞价员
  • 最专业的外贸网站建设关键词优化的策略
  • wordpress论坛优化惠东seo公司
  • 网站信息架构图怎么做windows优化大师卸载
  • 网上做批发有哪些网站靠谱吗网址搜索引擎入口
  • 广东商城网站建设公司seo百度百科