AtCoder Grand Contest 029
A - Irreversible operation
对于某个 W
的位置,它的贡献即为前面 B
的个数,直接搞就完事了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200005;
int n;
char s[N];
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
int cnt=0;
long long ans=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='B') cnt++;
else if(s[i]=='W') ans+=cnt;
}
printf("%lld",ans);
return 0;
}
B - Powers of two
对于每个数,加上一个小于等于它的数使得它等于 \(2\) 的幂次的数是唯一确定的。那么就可以将每个数向这个小于等于它的数连边,然后从叶子开始贪心匹配即可。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int N=200005;
int n;
int a[N];
unordered_map<int,int>cnt;
int b[N],tot;
vector<int>G[N];
int fa[N];
int ret[N];
int ans;
void dfs(int u,int father)
{
for(int v:G[u])
{
if(v==father) continue;
dfs(v,u);
if(ret[v]>0&&ret[u]>0)
{
int del=min(ret[v],ret[u]);
ret[v]-=del,ret[u]-=del,ans+=del;
}
}
if(__builtin_popcount(b[u]+b[u])==1) ans+=ret[u]/2,ret[u]%=2;
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
cnt[a[i]]++,b[++tot]=a[i];
sort(b+1,b+tot+1);
tot=unique(b+1,b+tot+1)-b-1;
for(int i=1;i<=tot;i++)
{
int t=log2(b[i])+1;
int val=(1<<t)-b[i];
if(!cnt[val]) continue;
int v=lower_bound(b+1,b+tot+1,val)-b;
if(i==v) continue;
G[i].emplace_back(v);
G[v].emplace_back(i);
fa[i]=v;
}
for(int i=1;i<=tot;i++)
ret[i]=cnt[b[i]];
for(int i=1;i<=n;i++)
if(!fa[i]) dfs(i,0);
printf("%d",ans);
return 0;
}
C - Lexicographic constraints
考虑二分答案,问题变成判断是否能用 \(k\) 个字符表示出所有的 \(S_i\)。
如果 \(A_{i-1} \lt A_i\),\(S_i\) 只要在 \(S_{i-1}\) 的基础上加上 \(A_i-A_{i-1}\) 个 a
即可。
如果 \(A_{i-1}\ge A_{i}\),\(S_i\) 只要在 \(S_{i-1}\) 的基础上去掉最后 \(A_{i-1}-A_i\) 个字符,然后将最后一个字符变成下一个字符。
那么就可以用一个栈来维护不是 a
的位置,最后只要判断下 \(1\) 有没有进位就好了。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=200005;
int n;
int a[N];
bool check(int x)
{
vector<pair<int,int>>S;
for(int i=1;i<=n;i++)
if(a[i]<=a[i-1])
{
while(!S.empty()&&S.back().second>a[i]) S.pop_back();
if(S.empty()) S.emplace_back(2,a[i]);
else if(S.back().second==a[i]) S.back().first++;
else S.emplace_back(2,a[i]);
while(!S.empty()&&S.back().first>x)
{
int c=S.back().second;
if(c==1) return false;
S.pop_back();
if(S.empty()) S.emplace_back(2,c-1);
else if(S.back().second==c-1) S.back().first++;
else S.emplace_back(2,c-1);
}
}
return true;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
bool flag=true;
for(int i=1;i<=n;i++)
if(a[i]<=a[i-1])
{
flag=false;
break;
}
if(flag)
{
printf("1");
return 0;
}
int l=2,r=n,ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
return 0;
}
D - Grid game
可以发现,第一个人是不会停下来的,否则第二个人也停下来游戏就结束了。
那么只需要算下到步数最少的那个障碍物的步数就行了(边界也算)。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=200005;
int h,w,n;
pair<int,int>a[N];
pair<int,int>c[N];
int cnt;
int main()
{
scanf("%d%d%d",&h,&w,&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].first,&a[i].second);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
if(a[i].first!=1&&a[i].first!=c[cnt].first) c[++cnt]=a[i];
int x=1,y=1;
for(int i=1;i<=cnt;i++)
{
auto [dx,dy]=c[i];
if(dy<y+(dx-x))
{
printf("%d",dx-1);
return 0;
}
else if(dy==y+(dx-x)) y=dy-1;
else y+=(dx-x);
x=dx;
}
printf("%d",h);
return 0;
}
E - Wandering TKHS
设点 \(u\) 到根的路径上分别是 \(u_1,u_2,u_3,\cdots ,u_{k−1},u_k\),考虑向上走的过程,从 \(u_i\to fa_{u_i}\) 就是 \(u\) 的子树中小于 \(fa_{u_i}\) 的点都走一遍。可以发现对于一个 \(i\in [1,n)\) 来说若存在 \(j\gt i\) 且 \(u_{j+1}>u_{i+1}\),那么后者的作用会包含前者,我们只要考虑每个点到根路径上的后缀最大值,也就是根到每个点路径上的前缀最大值。
那么就可以由父亲向儿子递推了,分成几种情况讨论下即可。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=200005;
int n;
vector<int>G[N];
int calc(int u,int father,int x)
{
int res=0;
for(int v:G[u])
{
if(v==father) continue;
if(v<x) res+=calc(v,u,x)+1;
}
return res;
}
int mx[N];
int ans[N];
void dfs(int u,int father)
{
mx[u]=max(mx[father],u);
for(int v:G[u])
{
if(v==father) continue;
ans[v]=ans[u];
if(u>mx[father])
{
if(v>mx[father]) ans[v]+=calc(v,u,u)+1;
else ans[v]+=calc(v,u,u)-calc(v,u,mx[father]);
}
else
{
if(v>mx[father]) ans[v]+=calc(v,u,mx[father])+1;
}
dfs(v,u);
}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
G[x].emplace_back(y);
G[y].emplace_back(x);
}
dfs(1,0);
for(int i=2;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}
F - Construction of a tree
我们考虑以某个点为根,对于每条边,看做是儿子和包含它的某个集合匹配。不妨把每个数看做左部点,每个集合看做右部点,一个集合和这个集合中的数连边,那么我们就会得到一个去掉根之后的完美匹配。可以发现这是一个二分图,用 dinic 跑二分图匹配构造方案即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int N=200005;
const int INF=1061109567;
int n;
struct Edge
{
int to,next,flow;
}edge[N*2+N*4];
int head[N<<1],cnt=1;
void add_edge(int u,int v,int f)
{
cnt++;
edge[cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].flow=f;
head[u]=cnt;
return;
}
void add(int u,int v,int f)
{
add_edge(u,v,f);
add_edge(v,u,0);
return;
}
int s,t;
int dep[N<<1];
bool bfs()
{
memset(dep,-1,sizeof(dep));
queue<int>q;
q.push(s);
dep[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(dep[v]!=-1||edge[i].flow<=0) continue;
dep[v]=dep[u]+1;
q.push(v);
}
}
return dep[t]!=-1;
}
int cur[N<<1];
int dfs(int u,int flow)
{
if(u==t||flow==0) return flow;
int used=0;
for(int &i=cur[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(dep[v]!=dep[u]+1||edge[i].flow<=0) continue;
int now=dfs(v,min(flow,edge[i].flow));
flow-=now;
edge[i].flow-=now;
edge[i^1].flow+=now;
used+=now;
if(flow==0) break;
}
return used;
}
int dinic()
{
int res=0;
while(bfs())
{
memcpy(cur,head,sizeof(head));
res+=dfs(s,INF);
}
return res;
}
vector<int>G[N];
int match[N];
bool vis[N];
pair<int,int>res[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int c;
scanf("%d",&c);
for(int j=1;j<=c;j++)
{
int x;
scanf("%d",&x);
if(x!=1) add(x,i+n,1);
G[x].emplace_back(i);
}
}
s=0,t=n+n-1+1;
for(int i=2;i<=n;i++)
add(s,i,1);
for(int i=1;i<=n-1;i++)
add(i+n,t,1);
int ans=dinic();
if(ans!=n-1)
{
printf("-1");
return 0;
}
for(int u=2;u<=n;u++)
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==s) continue;
if(edge[i].flow==0) match[v-n]=u;
}
queue<int>q;
q.emplace(1);
int tot=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int c:G[u])
if(!vis[c])
{
int v=match[c];
res[c]={u,v};
tot++;
q.emplace(v);
vis[c]=true;
}
}
if(tot!=n-1)
{
printf("-1");
return 0;
}
for(int i=1;i<=n-1;i++)
printf("%d %d\n",res[i].first,res[i].second);
return 0;
}