[Atcoder Regular Contest 064] Tutorial

时间:2022-10-12 16:15:10

Link:

ARC064 传送门

C:

贪心+对边界的特殊处理

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN=1e5+;
ll res=;
int n,x,dat[MAXN];
int main()
{
scanf("%d%d",&n,&x);
for(int i=;i<=n;i++) scanf("%d",&dat[i]);
if(dat[]>x) res+=dat[]-x,dat[]=x;
for(int i=;i<=n-;i++)
if(dat[i-]+dat[i]>x)
res+=dat[i-]+dat[i]-x,dat[i]=x-dat[i-];
if(dat[n-]+dat[n]>x) res+=dat[n-]+dat[n]-x;
printf("%lld",res);
return ;
}

Problem C

D:

一道结论题

博弈题一般都要先找到最终的必败/必胜态

对于此题,只要知道最终态的位数就能得到结果,但我一开始觉得位数不定就卡题了……

虽然无法确定最终必败态的位数,但能确定必败态位数的奇偶性

重要条件:序列的两端不能动

因此当两端相等时,最终态一定为$ababa$这样,位数为偶数个;当两端不等时位数一定为奇数个

这样判断初始序列和最终序列差值的奇偶性就能得到结果

#include <bits/stdc++.h>

using namespace std;
char s[];
int main()//最终个数难以确定时观察奇偶性
{
scanf("%s",s+);int len=strlen(s+);
if(s[]==s[len])
if(len&) puts("Second"); else puts("First");
else
if(len&) puts("First"); else puts("Second");
return ;
}

Problem D

由于答案由奇偶性判断,考虑最终态位数的奇偶性即可

E:

为了简化模型,想成走到圆心再从圆心出发,仍能保证最优解

于是两两连边跑最短路就行了(其实可以不用连边,每次找最近的走就行了)

#include <bits/stdc++.h>

using namespace std;
#define x first
#define y second
const int MAXN=;
const double INF=1e13,eps=1e-;
typedef pair<double,double> P;
P S,T,pos[MAXN];
vector<P> G[MAXN];
int n;double r[MAXN],d[MAXN]; double dist(P a,P b)
{return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));} int main()//其实可以不用连边,每次找最近的走就行了……
{
scanf("%lf%lf%lf%lf%d",&S.x,&S.y,&T.x,&T.y,&n);
for(int i=;i<=n;i++)
scanf("%lf%lf%lf",&pos[i].x,&pos[i].y,&r[i]);
pos[]=S;pos[n+]=T;
for(int i=;i<=n+;i++)
for(int j=;j<=n+;j++)
if(i!=j)
{
if(r[i]+r[j]>dist(pos[i],pos[j]))
G[i].push_back(P(j,));
else G[i].push_back(P(j,dist(pos[i],pos[j])-r[i]-r[j]));
} priority_queue<P,vector<P>,greater<P> > q;
for(int i=;i<MAXN;i++) d[i]=INF;
d[]=;q.push(P(,));
while(!q.empty())
{
P t=q.top();q.pop(); int u=t.y;
if(d[u]<t.x) continue;
for(int i=;i<G[u].size();i++)
{
int v=G[u][i].x;
if(d[v]>d[u]+G[u][i].y)
d[v]=d[u]+G[u][i].y,q.push(P(d[v],v));
}
}
printf("%.10lf",d[n+]);
return ;
}

Problem E

F:

一开始全在考虑如何去掉不合法的状态,但其实正向求解更方便

回文串共有$k^{n/2}$个,旋转后就有$k^{n/2}*n$个,但明显要去除重复的

观察到重复的原因是从一个回文串转移到了另一个回文串(或自己)

转移的步数与循环节有关:

(1)循环节为奇数时能转移$x$步,回到自己

(2)循环节为偶数时只能转移$x/2$步

因此只要计算构成每种循环节的组合数再乘上此时能转移的步数即可

令$dp[i]$表示最小循环节为$i$时的组合数,明显$dp[i]=k^{(i+1)/2}- \sum_{j|i} dp[j]$

(为了最终为回文数,每个循环节也要为回文数)

那么$res=\sum_{i|n} dp[i]*x(x/2)$

(Tip:$1e9$内因数最多的数的因数个数为1344,因此$O(d^2)$可行)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN=1e5+,MOD=1e9+;
int n,k,dvs[MAXN],dp[MAXN],tot,res=; ll quick_pow(ll a,ll b)
{
ll ret=;
for(;b;b>>=,a=a*a%MOD)
if(b&) ret=ret*a%MOD;
return ret;
} int main()
{
scanf("%d%d",&n,&k);
for(int i=;i*i<=n;i++)
if(n%i==)
{
dvs[++tot]=i;
if(i*i!=n) dvs[++tot]=n/i;
}
sort(dvs+,dvs+tot+);
for(int i=;i<=tot;i++)
{
dp[i]=quick_pow(k,(dvs[i]+)/);
for(int j=;j<=i-;j++)//对循环节长度的分类
if(dvs[i]%dvs[j]==) dp[i]=(dp[i]-dp[j]+MOD)%MOD;
if(dvs[i]%==) (res+=1ll*dp[i]*dvs[i]/%MOD)%=MOD;
else (res+=1ll*dp[i]*dvs[i]%MOD)%=MOD;
}
printf("%d",res);
return ;
}

Problem F

其实不算难的一道题目

当一种思路淦不出来时要赶紧换方向啊