比赛题单:2024“钉耙编程”中国大学生算法设计超级联赛(8)
(1004)HDU7520.cats 的重力拼图
题意
有一个 $n\times m$ 的方格阵列,物块初始位于 $(x,y),1\le x\le n,1\le y\le m$。
每次操作可以改变重力方向:向上、向下、向左、向右,物块会沿重力方向移动,直到碰到边界。
求任意操作下物块最多经过的格子数。
解题思路
有2种最贪心的操作序列:
- 向左、向右、再沿边缘一周
- 向上、向下、再沿边缘一周
特判 $n=1$ 或 $m=1$ 或初始在边缘的情况。
参考代码
1 2 3 4 5 6 7 8 9 10 11
| void solve() { ll n,m,a,b;cin >> n >> m >> a >> b; if(n<=2||m<=2){ cout << n*m << endl; return ; } ll ans=2*(n+m-2); if(a==1||a==n){ if(b==1||b==m) cout << ans << endl; else cout << ans+n-2 << endl; }else if(b==1||b==m) cout << ans+m-2 << endl; else cout << ans+max({0ll,n-2,m-2}) << endl; }
|
(1006)HDU7522.cats 的最小生成树
题意
给定一个有 $n$ 个节点,可能含重边的带权无向图, $m$ 条边按顺序给出,第 $i$ 条边的权值为 $i$。
每次删去当前图的最小生成树的所有边,直到图不连通。
求每条边是在第几次被删除去的。
解题思路
根据Kruskal算法思想,最小生成树加边是从小到大加入的。
开若干个并查集,遍历边,每次二分查找当前边最早可以加入第几个并查集。
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| void solve() { ll n,m;cin >> n >> m; ll ub=m/(n-1); vector<DSU> dsu(ub+2,DSU(n)); vector<ll> cnt(ub+2,0); vector<ll> ans(m+1,0); ll u,v; FORLL(i,1,m){ cin >> u >> v; ll l=1,r=ub+1; while(l<r){ ll mid=(l+r+1)/2; bool fl=(dsu[mid].find(u)==dsu[mid].find(v)); if(fl) l=mid; else r=mid-1; } ll tar=0; FORLL(i,max(1ll,l-3),min(ub+1,l+3)){ if(dsu[i].find(u)!=dsu[i].find(v)){ tar=i; break; } } if(tar==ub+1) { ans[i]=-1; continue; } dsu[tar].merge(u,v); cnt[tar]++; ans[i]=tar; } FORLL(i,1,m){ if(ans[i]!=-1&&cnt[ans[i]]==n-1) cout << ans[i]; else cout << "-1"; cout << Presentation(i,m); } }
|
(1007)HDU7523.cats 的 k-xor
题意
给定2个十进制整数 $a,b,c$ , $a,b$ 进行 $k(k\ge 2)$ 进制不进位加法后的结果是 $c$。
求 $k$ 有多少种可能。
解题思路
不进位加法下,丢失的进位信息 $a+b-c$ 是 $k$ 的倍数。
枚举 $a+b-c$ 的因子作为 $k$ ,check是否满足条件。
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| bool check(ll a,ll b,ll c,ll k){ ll dif=a+b-c; ll cur=1,nxt=k,s=0; while(a/cur||b/cur){ ll ta=a/cur%k,tb=b/cur%k; s+=(ta+tb)/k*nxt; cur*=k; nxt*=k; } return s==dif; } void solve() { ll a,b,c;cin >> a >> b >> c; ll dif=a+b-c; if(dif==0){ cout << "-1" << endl; return ; } ll cnt=0,ub=sqrt(dif)+1; FORLL(i,2,ub) if(dif%i==0){ if(i*i>dif) break; if(check(a,b,c,i)) cnt++; if(i*i!=dif&&check(a,b,c,dif/i)) cnt++; } if(check(a,b,c,dif)) cnt++; cout << cnt << endl; }
|
(1012)HDU7528.cats 的电脑中毒
题意
给定 $3$ 个长度为 $n$ 的二进制串 $a,b,c$ , 表示病毒的初始位置。
每过一秒,病毒会感染相邻的所有二进制编码。(当且仅当两个二进制编码仅有一个位置不同时,这两个编码为相邻)
问所有的 $2^n$ 个二进制编码都被感染需要多少时间。
解题思路
考虑最后一秒被感染的二进制串,它的 距离三个初始位置的最小汉明距离 最大,找到这个串。
设这个串为 $s$ ,枚举每一位。若三个初始位置的这一位上,1的数量较多,则设为0;否则设为1。
然后进行微调,使得 $s$ 到三个初始位置的最小汉明距离 最大。
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ll n; ll dis(string s1,string s2){ ll cnt=0; FORLL(i,0,n-1) cnt+=(s1[i]!=s2[i]); return cnt; } void solve() { cin >> n; string s[3],ns(n,'0'); FORLL(i,0,2) cin >> s[i]; ll cnta=0; FORLL(i,0,n-1){ int cnt1=0,t=0; FORLL(j,0,2) if(s[j][i]=='1') cnt1++; ns[i]=(cnt1>=2)?'0':'1'; } ll dis0=dis(s[0],ns),dis1=dis(s[1],ns),dis2=dis(s[2],ns); while(dis0<dis1-1&&dis0<dis2-1){ dis0++; dis1--; dis2--; } while(dis1<dis0-1&&dis1<dis2-1){ dis1++; dis0--; dis2--; } while(dis2<dis0-1&&dis2<dis1-1){ dis2++; dis0--; dis1--; } cout << min({dis0,dis1,dis2}) << endl; }
|