Codeforces Round 805 (Div. 3)

1|0基本情况

A、B、C题秒了。

D题一开始读错题了,以为是DP,后面发现是简单贪心,拖了点时间才AC。

不过无所谓,因为E题没思路了

但是总感觉 C 做的太不优雅。

2|0C. Train and Queries

我的做法

就纯用STL无脑模拟。跑了701ms

#include<iostream> #include<algorithm> #include<cstdio> #include<map> #include<queue> const int N = 2e5 + 10; int a[N]; int n, k; std::map<int, std::priority_queue <int,std::vector<int>,std::greater<int> > > tag1; std::map<int, std::priority_queue <int> > tag2; void solve(int l, int r) { if (tag1[l].empty() || tag2[r].empty()) std::cout << "NO\n"; else { if (tag1[l].top() < tag2[r].top()) std::cout << "YES\n"; else std::cout << "NO\n"; } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); int _;std::cin >> _; while(_--) { std::cin >> n >> k; tag1.clear();tag2.clear(); for (int i = 1; i <= n; i++) std::cin >> a[i], tag1[a[i]].push(i), tag2[a[i]].push(i); int a, b; while(k--) { std::cin >> a >> b; solve(a, b); } } return 0; }

2|1更优雅的

其实思想一样,只是本质上只要维护最先出现和最后出现的下标即可,我开两个优先队列显然是浪费,用两个数组维护就行了。

#include <bits/stdc++.h> using namespace std; int n,m,i,x,y; void solve(){ cin>>n>>m; map <int,int> a,b; for (i=1;i<=n;i++){ cin>>x; if (a[x]==0) a[x]=i; b[x]=i; } for (i=1;i<=m;i++){ cin>>x>>y; if (a[x]!=0 && b[y]!=0 && a[x]<=b[y]) cout<<"YES\n"; else cout<<"NO\n"; } } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int tests=1; cin>>tests; while (tests--) solve(); return 0; }

3|0E. Split Into Two Sets

要用到拓展域并查集或者二分图,待我先去学学。

3|1拓展域并查集解法

OK,周三学成归来。

那么这题确实可以用扩展域并查集来做。

因为依照题意,两个相同的数肯定不能在一起。

把一个数对的每个数构造出对应的敌人。

x_me = x, x_enemy = x + n; y_me = y, y_enemy = y + n;

然后一个数与另一个数的敌人合并。如果最后检索到一个数自己和这个数的敌人的祖先相同说明还是有两个相同的数在一起了,无法分为两组。

当然,如果相同的数的数目比二还大,那肯定也无法分为两组。

#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> using ll = long long; const int N = 2e5 + 10; int fa[N << 1]; int get(int x) {return fa[x] == x ? x : get(fa[x]);} void merge(int x, int y) { int fx = get(x), fy = get(y); if (fx == fy) return; fa[fx] = fy; } void init(int n) { for (int i = 1; i <= (n << 1); i++) fa[i] = i; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); int _, n; std::cin >> _; while(_--) { std::cin >> n; std::vector<int> tag(n + 1); init(n); for (int i = 1; i <= n; i++) { int x, y; std::cin >> x >> y; tag[x]++;tag[y]++; merge(x, y + n);merge(x + n, y); } bool ok = true; for (int i = 1; i <= n; i++) { if (tag[i] > 2) ok = false; if (get(i) == get(i + n)) ok = false; } std::cout << (ok ? "YES\n" : "NO\n"); } return 0; }

1|0二分图判定解法

OK,周四学成归来。

1|0思路

考虑将所有的不能放到一起的多米诺骨牌之间连上一条边,然后将整个图分成一个二分图就行了。

注意到这种方法可能会被有较多同样数字的多米诺骨牌的情况卡到 O(n2) 条边,于是想到如果出现有三个或者更多的相同数字的情况时直接判断无解,正确性显然。

总复杂度 O(n)

1|0代码

#include<vector> #include<cstdio> #include<algorithm> using namespace std; const int MAXN=2000005; int T,a[MAXN],b[MAXN],v[MAXN],flag,head[MAXN],num[MAXN*3],nxt[MAXN*3],tot,n,vis[MAXN]; void add(int x,int y){ nxt[++tot]=head[x]; head[x]=tot; num[tot]=y; } void work(int num,int p){ if(v[num]==-1){flag=1;return;} if(v[num]==0){v[num]=p;return;} add(v[num],p); add(p,v[num]); v[num]=-1; } void dfs(int now,int p){ vis[now]=p; for(int i=head[now];i;i=nxt[i]){ if(vis[num[i]]==p)flag=1; if(vis[num[i]]==0)dfs(num[i],3-p); } } int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) head[i]=0,v[i]=0,vis[i]=0; for(int i=1;i<=tot;i++) num[i]=nxt[i]=0; tot=0;flag=0; for(int i=1;i<=n;i++){ scanf("%d%d",&a[i],&b[i]); work(a[i],i);work(b[i],i); if(a[i]==b[i])flag=1; } for(int i=1;i<=n;i++) if(v[i]!=-1)flag=1; for(int i=1;i<=n;i++) if(vis[i]==0)dfs(i,1); if(flag==1)printf("NO\n"); if(flag==0)printf("YES\n"); } return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17878114.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示