【AC】atcoder grand contest 029
只做出了AB的菜鸡
AGC29
A题:给定一个只由BW构成的字符串,对于一次变换为BW变为WB,求这个字符串最多可以变换多少次。
B从右开始变,显然对于一个B可以变的次数由其右边有多少个W决定,倒着枚举即可。
#include<stdio.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
char ss[200005];
int n,cc;
long long ans;
int main() {
scanf("%s",&ss[1]);
n = strlen(ss+1);
for(int i=n;i>=1;i--){
if(ss[i]=='W') ++cc;
else ans += cc;
}
printf("%lld",ans);
}
B题:给定一串数,求最多有多少对(一个数只能被包含在一个对里面)使得他们的和为2的次幂。
很容易想到二分图然后就自闭。如果我们正着从小到大贪会发现有些小的可能使得多个更大的配对。那么我们倒着从大到小贪就可以了。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
int n;
map<int,int>ma;
int a[200005];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
ma[a[i]]++;
}
sort(a+1,a+1+n); int ans = 0;
for(int i=n;i>=1;i--) {
int x = a[i];
if(ma[x]!=0) {
for(int j=30;j>=1;j--) {
int o = (1<<j);
if(o<=x) break;
if(ma[o-x]!=0) {
if(o-x==x) {
if(ma[x]>=2) {ma[x]-=2,ans++;break; }
} else {
ma[x]--; ma[o-x]--; ans++; break;
}
}
}
}
}
printf("%d",ans);
}
C题,给定一个数列表示一个字符串数组每个字符的长度。使得满足这个字符串字典序单调递增,需要的最少的字符数。
二分答案+模拟进位即可。然而考场上模拟进位模拟挂了orz。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
const int maxn = 2e5 + 5;
int n;
int a[maxn];
map<int,int>s;
bool isok(int mid) {
s.clear();
for(int i=2;i<=n;i++) {
if(a[i]<=a[i-1]) {
while(!s.empty()&&((s.rbegin()->first) )>a[i]) {
s.erase(--s.end());
}
for(int j=a[i];j>=1;j--) {
if(s[j]+1==mid) {
s.erase(j);
} else {
s[j]++; break;
}
if(j==1) return 0;
}
}
}
return 1;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
bool fl = 1;
for(int i=2;i<=n;i++) {
if(a[i]<=a[i-1]) {
fl = 0; break;
}
}
if(n==1||fl) {
puts("1"); return 0;
}
int L = 2; int R = n,mid,ans;
while(L<=R){
mid = (L+R)>>1;
if(isok(mid)) ans = mid , R = mid - 1;
else L = mid + 1;
}
printf("%d",ans);
}
D题
一张图有一些障碍不能走,两个人交替走,一个只能向下走,一个只能往右走。如果出现一回合两个人都没走游戏结束。第一个人想游戏进行下去,第二个人想早点结束,求结束时间。
显然第一个人如果一步不走,那么游戏就结束了,所以他会一直往下。那么我们模拟第二个人能到的一个范围[1,r]如果存在某个障碍在下一排,那么游戏就结束了。
然而坑点是读懂英语orz
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int h,w,n;
int mh[200005];
int main() {
scanf("%d%d%d",&h,&w,&n);
for(int i=1;i<=h;i++) mh[i] = h+1;
for(int i=1;i<=n;i++) {
int x,y; scanf("%d%d",&x,&y);
mh[x] = min(mh[x],y);
}
int rr=1;
if(rr>=mh[2]) {
puts("1"); return 0;
}
for(int i=2;i<=h;i++) {
if(rr<=mh[i]-2) rr++;
if(rr>=mh[i+1]) {
printf("%d",i); return 0;
}
}
printf("%d",h);
}
E题,给定一颗编号从1到n的树,对于从某个点开始出发,他会选择走到和当前已走过的点相连中还未走过的点中的最小的那个点。对每个点求到1的次数。
显然,从某个点开始的答案和他的父亲相比(以1为根),总是单调不降,并且与其相差答案相关的一定是链前缀最大值(因为如果其到1的链上前缀最大值为x,那么从当前点出发的相连通的小于x的点一定会走到),那么我们从根开始搜一个个考虑,考虑前缀最大值带来的贡献,再差分减去父亲前缀最大值带来的贡献。具体记搜实现即可。
#include<stdio.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
const int maxn = 2e5 + 5;
map<int,int>dp[maxn];
vector<int>ve[maxn];
int n;
int ans[maxn];
int dfs2(int x,int ba,int v) {
if(dp[x].count(v)) return dp[x][v];
int sm = 1;
for(int y:ve[x]) {
if(y==ba) continue;
if(y<v) sm += dfs2(y,x,v);
}
return dp[x][v] = sm;
}
void dfs1(int x,int ba,int pr,int cc) {
cc += dfs2(x,ba,max(ba,pr));
if(pr>x) cc -= dfs2(x,ba,pr);
ans[x] = cc;
for(int y:ve[x]) {
if(y==ba) continue;
dfs1(y,x,max(pr,ba),cc);
}
}
int main() {
scanf("%d",&n);
for(int i=1;i<n;i++) {
int x,y; scanf("%d%d",&x,&y);
ve[x].push_back(y);
ve[y].push_back(x);
}
for(int y:ve[1]) {
dfs1(y,1,1,0);
}
for(int i=2;i<=n;i++) printf("%d ",ans[i]);
}
F题:给定n-1个由1-n的数构成的集合,从每个集合中选两个数出来当边,构造出一颗树,如果无解输出-1.
我们考虑对于每一条边都是(x , fax)的形式,也就是说对于除根以外的每一个数都作为x在集合里出现过一次。这样的话我们直接考虑利用匈牙利(或网络流)二分图匹配出每个集合对应的方案,然后从根节点开始考虑对其所在的所有集合中的x连边。对于这样构造出的一种方案,如果不存在这样构造出的方案,可以想到这说明这棵树一定不是联通的。
#include<stdio.h>
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 200005;
int n;
vector<int>ntoj[maxn],jton[maxn];
int lk[maxn],dy[maxn];
int vis[maxn];
bool hungary(int x,int id) {
if(vis[x]==id) return 0;
vis[x] = id;
for(int y:jton[x]) {
if(!lk[y]) {
lk[y] = x;
return 1;
}
}
for(int y:jton[x]) {
if(hungary(lk[y],id)) {
lk[y] = x;
return 1;
}
}
return 0;
}
void wuj() {
puts("-1");
exit(0);
}
bool vi[maxn];
int fr[maxn],to[maxn];
void dfs(int x) {
vi[x] = 1;
for(int y:ntoj[x]) {
if(!vi[dy[y]]) {
fr[y] = x;
to[y] = dy[y];
dfs(dy[y]);
}
}
}
int main() {
scanf("%d",&n);
for(int i=1;i<n;i++) {
int sz; scanf("%d",&sz);
jton[i].resize(sz);
for(int j=0;j<sz;j++) {
int x; scanf("%d",&x);
ntoj[x].push_back(i);
jton[i][j]=x;
}
if((!hungary(i,i))) wuj();
}
for(int i=1;i<=n;i++) {
dy[lk[i]]=i;
}
int rt = 1;
while(lk[rt]) rt++;
dfs(rt);
for(int i=1;i<=n;i++) {
if(!vi[i]) wuj();
}
for(int i=1;i<n;i++) {
printf("%d %d\n",fr[i],to[i]);
}
}
小结:这场比赛其实本身没有什么没有学过的高端算法,代码量也极其的小,主要是在考思维难度上,但仔细想每道题的思路又是自然而成,而没有过多的跳跃性。感受到被各方dalao们狂虐的感觉orz,说明在思维上还是没有放开,总是在追寻着一个套路,而没有将题目进一步转化。

浙公网安备 33010602011771号