AtCoder Grand Contest 034
Kenken Race
可以分成两种情况:
- 当 \(A\leq B\leq C\leq D\) 时,先让 \(B\) 到 \(D\),在让 \(A\) 到 \(C\);
- 当 \(A\leq B\leq D\leq C\) 时,判断一下 \(B\to D\) 是否有三个连续的
.
。
然后判断一下 \(A\to C,B\to D\) 是否有两个连续的 #
即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int N=200005;
int n,a,b,c,d;
char s[N];
int main()
{
scanf("%d%d%d%d%d",&n,&a,&b,&c,&d); //a->c b->d
scanf("%s",s+1);
if(a<b&&c>d)
{
bool flag=false;
for(int i=b;i<=d;i++)
if(s[i-1]=='.'&&s[i]=='.'&&s[i+1]=='.')
{
flag=true;
break;
}
if(!flag)
{
printf("No");
return 0;
}
for(int i=a+1;i<=c;i++)
if(s[i-1]=='#'&&s[i]=='#')
{
printf("No");
return 0;
}
printf("Yes");
return 0;
}
else
{
for(int i=b+1;i<=d;i++)
if(s[i-1]=='#'&&s[i]=='#')
{
printf("No");
return 0;
}
for(int i=a+1;i<=c;i++)
if(s[i-1]=='#'&&s[i]=='#')
{
printf("No");
return 0;
}
printf("Yes");
return 0;
}
return 0;
}
B - ABC
分成 BC
和 A
,每个 BC
都可以跟它前面的相邻 A
匹配,记录一下前面连续的 A
的数量,遇到单个的 B
,C
清空即可。
#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 A=0;
long long ans=0;
for(int i=1;i<=n;i++)
if(s[i]=='B'&&s[i+1]=='C') ans+=A,i++;
else if(s[i]=='A') A++;
else if(s[i]=='B'||s[i]=='C') A=0;
printf("%lld",ans);
return 0;
}
C - Tests
可以发现,重要度只可能为 \(u_i\) 和 \(l_i\) 中的一个,如果 \(a_i\ge b_i\) 选 \(u_i\),\(a_i< b_i\) 选 \(l_i\)。
可以二分一个 \(mid\),判断学习 \(mid\) 后能否合法。可以发现,我们肯定是将一个前缀 \(a_i\) 置为 \(X\),一个后缀 \(a_i\) 置为 \(0\),中间有一个 \(0\sim X\) 的 \(a_i\)。
我们肯定贪心的使收益最大的在前面。但是选 \(u_i\) 和 \(l_i\) 时 Aoki 的得分是不同的,不妨将 \(\le l_i\) 的部分将两个人都看做 \(l_i\) 的贡献。那么对 \(i\) 门科目,对于 \(a_i\ge b_i\) 的贡献为 \((a_i-b_i)u_i+b_il_i\),\(a_i< b_i\) 的贡献为 \(a_il_i\)。
将所有科目按照 \((X-b_i)u_i+b_il_i\) 排序,枚举取 \(0\sim X\) 的 \(a_i\) 的位置 \(i\),然后贪心的填即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100005;
int n,X;
struct Node
{
int b,l,u;
long long v;
}a[N];
long long sum[N];
long long A,B;
bool check(long long x)
{
int t=min(x/X,(long long)n),ret=x%X;
for(int i=1;i<=t;i++)
{
long long add=0;
if(ret>a[i].b) add=1LL*(ret-a[i].b)*a[i].u+1LL*a[i].b*a[i].l;
else add=1LL*ret*a[i].l;
if(sum[min(t+1,n)]-a[i].v+add>=B) return true;
}
for(int i=t+1;i<=n;i++)
{
long long add=0;
if(ret>a[i].b) add=1LL*(ret-a[i].b)*a[i].u+1LL*a[i].b*a[i].l;
else add=1LL*ret*a[i].l;
if(sum[t]+add>=B) return true;
}
return sum[t]>=B;
}
int main()
{
scanf("%d%d",&n,&X);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a[i].b,&a[i].l,&a[i].u);
a[i].v=1LL*(X-a[i].b)*a[i].u+1LL*a[i].b*a[i].l;
}
sort(a+1,a+n+1,[=](const Node &x,const Node &y){return x.v>y.v;});
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i].v;
for(int i=1;i<=n;i++)
B+=1LL*a[i].b*a[i].l;
long long l=0,r=1e10,ans=-1;
while(l<=r)
{
long long mid=(l+r)/2;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld",ans);
return 0;
}
D - Manhattan Max Matching
直接连边的话边数有 \(n^2\) 条,无法通过。
将绝对值拆开:
可以发现两个点是独立的:
中间建四个点,分别表示四种情况,分别连边然后跑最大费用最大流即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=2010;
const int INF=1061109567;
const long long LINF=4557430888798830399;
int n;
int rx[N],ry[N],rc[N];
int bx[N],by[N],bc[N];
struct Edge
{
int to,next;
long long flow,cost;
}edge[N*10];
int head[N],cur[N],cnt=1;
void add_edge(int u,int v,int c,int f)
{
cnt++;
edge[cnt].to=v;
edge[cnt].cost=c;
edge[cnt].flow=f;
edge[cnt].next=head[u];
head[u]=cnt;
return;
}
void add(int u,int v,int c,int f)
{
add_edge(u,v,c,f);
add_edge(v,u,-c,0);
return;
}
int s,t;
long long dis[N];
bool spfa()
{
static bool vis[N];
memset(vis,false,sizeof(vis));
for(int i=0;i<=2*n+5;i++)
dis[i]=-LINF;
vis[s]=true;
dis[s]=0;
queue<int>q;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].flow<=0) continue;
if(dis[v]<dis[u]+edge[i].cost)
{
dis[v]=dis[u]+edge[i].cost;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
return dis[t]!=-LINF;
}
bool book[N];
pair<long long,long long> dfs(int u,long long flow)
{
if(u==t||flow==0) return make_pair(flow,0);
book[u]=true;
long long used=0,res=0;
for(int &i=cur[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(book[v]||dis[v]!=dis[u]+edge[i].cost||edge[i].flow<=0) continue;
pair<long long,long long>t=dfs(v,min(flow,edge[i].flow));
long long now=t.first;
res+=t.second+now*edge[i].cost;
flow-=now;
edge[i].flow-=now;
edge[i^1].flow+=now;
used+=now;
if(flow==0) break;
}
book[u]=false;
return make_pair(used,res);
}
pair<long long,long long> dinic()
{
long long ans=0,Min=0;
while(spfa())
{
memcpy(cur,head,sizeof(head));
pair<long long,long long> res=dfs(s,INF);
ans+=res.first,Min+=res.second;
}
return make_pair(ans,Min);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&rx[i],&ry[i],&rc[i]);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&bx[i],&by[i],&bc[i]);
s=0,t=2*n+1;
int p1=2*n+2,p2=2*n+3,p3=2*n+4,p4=2*n+5;
for(int i=1;i<=n;i++)
{
add(s,i,0,rc[i]);
add(i,p1,rx[i]+ry[i],INF);
add(i,p2,rx[i]-ry[i],INF);
add(i,p3,-rx[i]+ry[i],INF);
add(i,p4,-rx[i]-ry[i],INF);
}
for(int i=1;i<=n;i++)
{
add(i+n,t,0,bc[i]);
add(p1,i+n,-bx[i]-by[i],INF);
add(p2,i+n,-bx[i]+by[i],INF);
add(p3,i+n,bx[i]-by[i],INF);
add(p4,i+n,bx[i]+by[i],INF);
}
long long ans=dinic().second;
printf("%lld",ans);
return 0;
}
E - Complete Compress
枚举最后到了哪个点,判断这个点是否合法。
可以发现,我们肯定只会进行两种操作,一种是将两个点的深度同时减一,一种是将一个点的深度减一,一个点的深度加一。仔细思考后发现第二种其实也是没用的,因为显然可以将所有二操作移到最后,最后也就不能移了。
令 \(Min_i,Max_i\) 表示考虑 \(i\) 子树中的所有的棋子离 \(i\) 的最小距离和,最大距离和。最大距离和即为所有的棋子到 \(i\) 的距离,考虑得出最小距离和。
对于子树中所有点到根的深度 \(d_1,d_2,\cdots ,d_n\),操作等价于将 \(d_i,d_j\ (i\neq j)\) 分别减一。令 \(S\) 为 \(d_i\) 的和,则如果 \(\max\{d\}-(S-\max\{d\})\ge 0\),最后剩下的即为 \(\max\{d\}-(S-\max\{d\})\),否则剩下的即为 \(\max\{d\} \bmod 2\)。
我们需要将最后剩下的等于 \(0\),则如果 \(\max\{d\}-(S-\max\{d\})\ge 0\),我们可以递归 \(\max\{d\}\) 的子树,将 \(d\) 减少为 \(Min\)。
判断根节点的 \(Min\) 是否为 \(0\) 即可。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=2005;
const int INF=1061109567;
int n;
int a[N];
vector<int>G[N];
long long Min[N],Max[N];
int siz[N];
int son[N];
void dfs1(int u,int father)
{
siz[u]=0;
if(a[u]) siz[u]++;
Max[u]=0;
son[u]=0;
for(int v:G[u])
{
if(v==father) continue;
dfs1(v,u);
siz[u]+=siz[v];
Max[u]+=Max[v]+siz[v];
if(Max[v]>Max[son[u]]) son[u]=v;
}
return;
}
void dfs2(int u,int father)
{
if(!son[u])
{
Min[u]=0;
return;
}
dfs2(son[u],u);
int mi=Min[son[u]]+siz[son[u]],mx=Max[son[u]]+siz[son[u]];
if(mi-(Max[u]-mx)>=0) Min[u]=mi-(Max[u]-mx);
else Min[u]=Max[u]&1;
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%1d",&a[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
Max[0]=-1;
long long ans=INF;
for(int u=1;u<=n;u++)
{
dfs1(u,0);
dfs2(u,0);
if(Min[u]==0) ans=min(ans,Max[u]/2);
}
if(ans>=INF) printf("-1");
else printf("%lld",ans);
return 0;
}
F - RNG and XOR
咕咕咕。