AtCoder Grand Contest 019
链接
C. Fountain Walk
首先毛估估一下,可以发现我们一定不会为了蹭柱子而绕远路。而在最优策略中每经过一次柱子一定会向另一个方向转。
所以直接统计路上最多能蹭到几次柱子,这就是最长上升子序列。
特别的可能会出现路被柱子堵死的情况,这样需要绕过一根柱子,特判额外贡献。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 200010
#define M 200000000
#define db double
using namespace std;
const db pi=acos(-1),dt=10*(2-pi/2);
struct node{
int x,y;
bool operator <(const node a)const{return x<a.x;}
}p[N];
int q[N],qt;
int main()
{
int x1,y1,x2,y2,n;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&n);
bool tx=0,ty=0;
if(x1>x2) x1=M-x1,x2=M-x2,tx=true;
if(y1>y2) y1=M-y1,y2=M-y2,ty=true;
int tt=0;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(tx) x=M-x;if(ty) y=M-y;
if(x1<=x && x<=x2 && y1<=y && y<=y2) p[++tt]=(node){x,y};
}
sort(p+1,p+tt+1);
db d=(x2-x1+y2-y1)*100.0;
memset(q,0x3f,sizeof(q));
for(int i=1;i<=tt;i++)
{
int l=lower_bound(q+1,q+tt+1,p[i].y)-q;
q[l]=p[i].y;
}
int res=0;
for(int i=1;i<=tt;i++)
if(q[i]<=M) res++;
if(res==x2-x1+1 || res==y2-y1+1) d+=5*pi;
printf("%.11lf",d-dt*res);
return 0;
}
D. Shift and Flip
首先考虑贪心,最后一定是往某个方向转一定位置,然后往一直另一个方向转。
可以证明这样一定是最优的。最优方案就是总位移加上需要改变的颜色数。
考虑枚举最后的对应位置,那么一个需要改变的点需要在转动的过程中遇到 才行。
直接处理出每个点向左向右最近的黑点位置。总复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2010
using namespace std;
char s[N],t[N];
int l[N],r[N],mx[N],ans;
void solve(int n)
{
// printf("%s\n%s\n",s,t);
for(int i=0;i<n;i++)
{
for(r[i]=0;t[(i+r[i])%n]!='1';r[i]++);
for(l[i]=0;t[(i-l[i]+n)%n]!='1';l[i]++);
}
for(int i=0;i<n;i++)
{
memset(mx,0,sizeof(mx));
int res=0,m=0;
for(int j=0;j<n;j++)
if(s[j]!=t[(i+j)%n])
{
res++;
mx[l[j]]=max(mx[l[j]],r[j]);
}
for(int j=n-1;j>=0;j--) ans=min(ans,2*j+m+abs(m-i)+res),m=max(m,mx[j]);
ans=min(ans,res+m*2+i);
}
}
int main()
{
scanf("%s%s",s,t);
int n=strlen(s);ans=2*n;
bool zs=true,zt=true;
for(int i=0;i<n;i++) if(s[i]=='1') zs=false;
for(int i=0;i<n;i++) if(t[i]=='1') zt=false;
if(zt){puts(zs?"0":"-1");return 0;}
solve(n);reverse(s,s+n);reverse(t,t+n);solve(n);
printf("%d\n",ans);
return 0;
}
E. Shuffle and Swap
考虑可以把题目转化为:有 种点,其中第一种点可以连出一条边,第二种点可以连出并连入各一条边,第三种点可以连入一条边。
可以发现除了第二种点组成的环之外,其余的点只要确定了连边方案,那么连边顺序也是唯一的。第二种点组成的环的所有方案恰好是 。
所以考虑用 表示选了 个二类点, 组一三类点的方案数。要求 ,那么剩下的二类点就可以任意选择。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 10010
#define M 5010
#define mod 998244353
using namespace std;
int fac[N],inv[N];
int ksm(int a,int b=mod-2)
{
int r=1;
for(;b;b>>=1)
{
if(b&1) r=1ll*r*a%mod;
a=1ll*a*a%mod;
}
return r;
}
void init(int n=N-10)
{
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[n]=ksm(fac[n]);
for(int i=n-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
int C(int x,int y){return x<y?0:1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
char s[N],t[N];
int s1,s2;
int f[M][N];
int main()
{
scanf("%s%s",s+1,t+1);
int n=strlen(s+1);
init();
for(int i=1;i<=n;i++)
if(s[i]==t[i]) s1+=s[i]=='1';
else s2+=s[i]=='1';
for(int i=0;i<=s2;i++) f[i][0]=1ll*fac[i]*fac[i]%mod;
for(int i=1;i<=s2;i++)
for(int j=1;j<=s1;j++)
f[i][j]=(1ll*f[i][j-1]*i%mod*j%mod+1ll*f[i-1][j]*i%mod*i%mod)%mod;
int ans=0;
for(int i=0;i<=s1;i++) ans=(ans+1ll*f[s2][s1-i]*fac[i]%mod*fac[i]%mod*C(s1,i)%mod*C(s1+s2,i)%mod)%mod;
printf("%d\n",ans);
return 0;
}
F. Yes or No
好水的 。
考虑选择的策略应该是怎样的。显然如果对的比错的多应该选对,否则选错。这样可以发现如果没有越过中间部分,答案就是 。
考虑越过中间部分即对错一样时我们一定有 的概率选对。可以发现无论这次对不对,都不会影响那 部分的答案。
所以直接用每个点被经过的次数和除以总方案数乘 就是这部分的贡献。
最后加上 即可。复杂度 。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define N 200010
#define M 800010
#define inf 100000000
using namespace std;
int nxt[M<<1],to[M<<1],w[M<<1],head[N],cnt=1;
void add(int u,int v,int f)
{
nxt[++cnt]=head[u];to[cnt]=v;
w[cnt]=f;head[u]=cnt;
}
void adde(int u,int v,int w1=inf,int w2=0){add(u,v,w1);add(v,u,w2);}
int all,dep[N],cur[N];
queue<int>q;
bool bfs(int s,int t)
{
for(int i=1;i<=all;i++) dep[i]=0,cur[i]=head[i];
q.push(s);dep[s]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=nxt[i])
if(w[i] && !dep[to[i]]) dep[to[i]]=dep[u]+1,q.push(to[i]);
}
return dep[t];
}
int dfs(int u,int t,int flow=inf)
{
if(u==t || !flow) return flow;
int res=0;
for(int &i=cur[u];i;i=nxt[i])
{
int v=to[i],f;
if(dep[v]==dep[u]+1 && (f=dfs(v,t,min(flow,w[i]))))
{
flow-=f,res+=f;
w[i]-=f,w[i^1]+=f;
if(!flow) break;
}
}
return res;
}
int dinic(int s,int t){int res=0;while(bfs(s,t)) res+=dfs(s,t);return res;}
int s,t;
int id[N],ax[N],ay[N],tt;
void dfs(int u)
{
++tt;
for(int i=head[u];i;i=nxt[i])
if(w[i])
{
int v=to[i];
if(v==s || id[v]) continue;
id[v]=u;
for(int j=head[v];j;j=nxt[j])
if(w[j])
{
ax[v]=u,ay[v]=to[j];
dfs(to[j]);
}
}
}
int main()
{
int n;
scanf("%d",&n);s=2*n,t=s+1;all=t;
for(int i=1;i<=n;i++) adde(s,i,1);
for(int i=1;i<n;i++) adde(i+n,t,1);
for(int i=1;i<n;i++)
{
int c,a;
scanf("%d",&c);
while(c --> 0) scanf("%d",&a),adde(a,i+n,1);
}
if(dinic(s,t)!=n-1){puts("-1");return 0;}
for(int i=head[s];i;i=nxt[i])
if(w[i]){dfs(to[i]);break;}
if(tt!=n){puts("-1");return 0;}
for(int i=n+1;i<n*2;i++) printf("%d %d\n",ax[i],ay[i]);
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/agc019.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理