Loading

[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;
    }
    
posted @ 2022-03-13 12:05  xkjie  阅读(216)  评论(0)    收藏  举报