[Atcoder]ABC243题解
ABC243解题报告
D - Moves on Binary Tree
如果直接算的话,必然运算过程中必然会爆long long.
注意到一次'L'之后再来一个'U'等价于没有做操作.
对于'R'同理.
那么我们只需要在计算之前先将这一类情况消去.
这样就可以直接得出答案(\(ans<=1e18\))
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define int long long
using namespace std;
int n,x,totu,stk[1000010],cntk;
bool f[1000010];
char s[1000010];
signed main()
{
scanf("%lld %lld %s",&n,&x,s+1);
for (int i=n;i>0;--i)
{
if (s[i]=='U')
{
totu++;
stk[++cntk]=i;
}
else if (totu>0)
{
totu--;
f[i]=1;
f[stk[cntk]]=1;
cntk--;
}
}
for (int i=1;i<=n;i++)
{
if (f[i]) continue;
if (s[i]=='L') x*=2;
else if (s[i]=='R') x=x*2+1;
else x/=2;
}
printf("%lld",x);
//system("pause");
return 0;
}
E -Edge Deletion
因为边权全部为正整数,那么如果一条边的两个顶点的最短路不经过这条边,那么这条边就可以删去.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#define N 310
#define M 45010
#define int long long
using namespace std;
struct node
{
int a,b,c;
}k[M];
int n,m,mp[N][N];
signed main()
{
scanf("%lld %lld",&n,&m);
memset(mp,0x3f,sizeof(mp));
for (int i=1;i<=n;i++)
mp[i][i]=0;
for (int i=1;i<=m;i++)
{
scanf("%lld %lld %lld",&k[i].a,&k[i].b,&k[i].c);
mp[k[i].a][k[i].b]=k[i].c;
mp[k[i].b][k[i].a]=k[i].c;
}
for (int s=1;s<=n;s++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
mp[i][j]=min(mp[i][j],mp[i][s]+mp[s][j]);
int ans=0;
for (int i=1;i<=m;i++)
{
int deln=0;
for (int q=1;q<=n;q++)
{
if (q==k[i].a || q==k[i].b) continue;
if (mp[k[i].a][q]+mp[q][k[i].b]<=k[i].c)
deln=1;
}
ans+=deln;
}
printf("%lld",ans);
//system("pause");
return 0;
}
另外一种实现方法:
在floyd的时候pre数组记录点a,b能被k松弛.
即a,b的最短路一定经过点k.
那么如果a,b为一条边的两个顶点,并且\(pre\big[a\big]\big[b\big]==a\) \(||\) \(pre\big[a\big]\big[b\big]==b\)
则这条边在a,b的最短路上,也就不能删去
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 3e2 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll dis[maxn][maxn];
int pre[maxn][maxn];
void work()
{
cin >> n >> m;
mem(dis, 0x3f);mem(pre, -1);
for(int i = 1; i <= m; ++i){
int x, y, z;cin >> x >> y >> z;
dis[x][y] = dis[y][x] = min(dis[x][y], 1ll * z);
pre[x][y] = pre[y][x] = y;
}
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
{
if(dis[i][j] >= dis[i][k] + dis[k][j]){
dis[i][j] = dis[i][k] + dis[k][j];
pre[i][j] = k;
}
}
int ans = m;
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
if(pre[i][j] == i || pre[i][j] == j)
--ans;
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
第二种方法的代码引自__Rain
G - Sqrt
显然有限的次数之后\(x\)会变为1,那么之后的操作次数就不需要关心了
-
\(O(x^\frac{1}{2})\)的做法
设\(dp[x]\)表示以x开头的种类数. 那么有以下状态转移方程:
\(dp[x]=\Sigma_{i=1}^{\lfloor \sqrt{x} \rfloor} dp[i]\)
-
\(O(x^\frac{1}{4})\)的做法
上述做法对于\(9*10^{18}\)的数据范围来说还不够优秀.所以我们要进一步优化.注意到我们如果知道了\(A_1=x,A_3=i\),那么\(A_2\)可以为\(i^2\)~\(\sqrt{x}\)中的任何一个数
那么就有\(dp[x]=\Sigma_{i=1}^{\lfloor x^{\frac{1}{4}} \rfloor} ({\lfloor \sqrt{x} \rfloor}-i^2+1)dp[i]\)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define ll long long
#define ld long double
#define rep(i,n,m) for(ll i=n;i<=m;++i)
using namespace std;
int t;
ll x,a[25],dp[100000],mx;
int main()
{
scanf("%d",&t);
rep(i,1,t)
{
scanf("%lld",&a[i]);
mx=max(a[i],mx);
}
ll len=(ll)(sqrt(sqrt((ld)mx)));
dp[1]=1;
rep(i,2,len)
{
ll l=(ll)(sqrt(sqrt((ld)i))),l2=(ll)(sqrt((ld)i));
rep(j,1,l)
dp[i]+=(l2-j*j+1)*dp[j];
}
rep(i,1,t)
{
ll ans=0,l=(ll)(sqrt(sqrt((ld)a[i]))),l2=(ll)(sqrt((ld)a[i]));
rep(j,1,l)
ans+=(l2-j*j+1)*dp[j];
printf("%lld\n",ans);
}
//system("pause");
return 0;
}
-
tips:开根号的时候会出现精度问题.所以我将\(a[i]\)从\(long\) \(long\)强制转为了\(long\) \(double\).
我调了一个小时才发现是这里的问题或者也可以用以下函数代替原本的sqrt来规避精度问题
ll isqrt(ll N){ ll sqrtN=sqrt(N)-1; while(sqrtN+1<=N/(sqrtN+1))sqrtN++; return sqrtN; }