2019 ICPC Asia Xuzhou Regional

比赛链接:https://www.jisuanke.com/contest/14670/challenges

A. Cat (思维)

题意:有 1e18 只猫在排成一列。第 i 只猫的价格为 i 。假设要买区间 [ l , r ] [l,r] [l,r] 的所有猫,花费的价格为 l ⊕ ( l + 1 ) ⋯ ⊕ ( r − 1 ) ⊕ r l\oplus (l+1) \dots \oplus(r-1) \oplus r l(l+1)(r1)r 。现在有 n 个询问,每次询问为 ( L , R , S ) (L,R,S) (L,R,S) ,问在区间 [ L , R ] [L,R] [L,R] 中只能选择连续的一段买猫,用 S 元钱最多能够买几只猫

思路:连续的买,4 个为一组异或为 0 。然后枚举一下左右两边多出来的猫即可

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int t;
ll L,R,S;
vector<ll> vec;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld %lld %lld",&L,&R,&S);
        if(R-L+1<8)
        {
            ll ans=-1;
            for(ll i=L; i<=R; ++i)
            {
                ll cost=0;
                for(ll j=i; j<=R; ++j)
                {
                    cost^=j;
                    if(cost<=S) ans=max(ans,1ll*j-i+1);
                }
            }
            printf("%lld\n",ans);
        }
        else
        {
            ll l=L,r=R;
            vec.clear();
            while(l%4!=0&&l<=R) l++;
            while(r%4!=3) r--;
            for(ll i=L; i<l; ++i) vec.push_back(i);
            for(ll i=r+1; i<=R; ++i) vec.push_back(i);
            ll ans=r-l+1;
            int n=vec.size();
            ll res=0;
            for(int i=0; i<n; ++i)
            {
                ll cost=0;
                for(int j=i; j<n; ++j)
                {
                    cost^=vec[j];
                    if(cost<=S) res=max(res,1ll*j-i+1);
                }
            }
            printf("%lld\n",ans+res);
        }
    }
    return 0;
}

C. < 3 <3 <3 numbers (素数分布)

题意:询问在区间 [ L , R ] [L,R] [L,R] 内素数和 1 的个数是否小于 1 3 \frac 13 31

思路:在区间很大的时候显然满足,区间小的时候暴力判断一下

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int t,L,R;
bool check(int x)
{
    if(x==1) return 1;
    for(int i=2; i*i<=x; ++i)
        if(x%i==0) return 0;
    return 1;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&L,&R);
        if(R-L+1>=10000) puts("Yes");
        else
        {
            int res=0;
            for(int i=L; i<=R; ++i)
                if(check(i)) res++;
            bool ok=(3*res<R-L+1);
            puts(ok?"Yes":"No");
        }
    }
    return 0;
}

E. Multiply (大数质因子分解)

题意:给定 n、X、Y 和 a 数组,问满足 a 1 ! a 2 ! … a n ! X i a_1!a_2!\dots a_n! X^i a1!a2!an!Xi 整除 Y ! Y! Y! 的最大的 i 是多少? ( 1 ≤ X , Y , a i ≤ 1 0 18 ) (1\le X,Y,a_i \le 10^{18}) (1X,Y,ai1018)

思路:其实就是使下面这个式子能够整除。

Y ! a 1 ! a 2 ! … a n ! X i \frac {Y!}{a_1!a_2!\dots a_n! X^i} a1!a2!an!XiY!

  • 这里只需要关注 X X X 的素因子。计算 Y ! Y! Y! 中的所有和 X X X 相关的素因子,除去 a 1 ! a 2 ! … a n ! a_1!a_2!\dots a_n! a1!a2!an! 中的因子之后,剩余的素因子能够满足 X 的数量就是答案。
  • X 的值很大需要用到大数的质因子分解
#include <bits/stdc++.h>
#define fi first
#define se second
#define ll long long
using namespace std;
const int maxn=1e5+5;

ll x[105];
ll multi(ll a, ll b, ll p)
{
    ll ans = 0;
    while(b)
    {
        if(b & 1LL) ans = (ans+a)%p;
        a = (a+a)%p;
        b >>= 1;
    }
    return ans;
}

ll qpow(ll a, ll b, ll p)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1LL) ans = multi(ans, a, p);
        a = multi(a, a, p);
        b >>= 1;
    }
    return ans;
}

bool MR(ll n)
{
    if(n == 2) return true;
    int s = 20, i, t = 0;
    ll u = n-1;
    while(!(u&1))
    {
        t++;
        u >>= 1;
    }
    while(s--)
    {
        ll a = rand()%(n-2)+2;
        x[0] = qpow(a, u, n);
        for(i = 1; i <= t; i++)
        {
            x[i] = multi(x[i-1], x[i-1], n);
            if(x[i] == 1 && x[i-1] != 1 && x[i-1] != n-1)
                return false;
        }
        if(x[t] != 1) return false;
    }
    return true;
}

ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}

ll Pollard_Rho(ll n, int c)
{
    ll i = 1, k = 2, x = rand()%(n-1)+1, y = x;
    while(1)
    {
        i++;
        x = (multi(x, x, n)+c)%n;
        ll p = gcd((y-x+n)%n, n);
        if(p != 1 && p != n) return p;
        if(y == x) return n;
        if(i == k)
        {
            y = x;
            k <<= 1;
        }
    }
}
map<ll,ll> m,mp;
void find(ll n, int c=12345)
{
    if(n == 1) return;
    if(MR(n))
    {
        m[n]++;
        return;
    }
    ll p = n, k = c;
    while(p >= n) p = Pollard_Rho(p, c--);
    find(p, k);
    find(n/p, k);
}

ll t,n,X,Y;
ll a[maxn];
ll solve(ll p,ll n)
{
    ll res=0,now=p;
    while(now<=n)
    {
        res+=n/now;
        if(now<=n/p) now*=p;
        else break;
    }
    return res;
}
int main()
{
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld %lld %lld",&n,&X,&Y);
        for(int i=1; i<=n; ++i) scanf("%lld",&a[i]);
        m.clear();
        mp.clear();
        find(X);
        for(auto x: m)
            mp[x.fi]=solve(x.fi,Y);
        for(int i=1; i<=n; ++i)
            for(auto x: m)
                mp[x.fi]-=solve(x.fi,a[i]);
        ll ans=1e18;
        for(auto x: m)
            ans=min(ans,mp[x.fi]/x.se);
        printf("%lld\n",ans);
    }
    return 0;
}

F. The Answer to the Ultimate Question of Life, The Universe, and Everything. (打表)

题意:给定一个 x ∈ [ 0 , 200 ] x\in [0,200] x[0,200] ,输出一组 (a,b,c)满足 a 3 + b 3 + c 3 = x a^3+b^3+c^3 =x a3+b3+c3=x 0 ≤ ∣ a ∣ , ∣ b ∣ , ∣ c ∣ ≤ 5000 0\le |a|,|b|,|c| \le 5000 0a,b,c5000

思路: x 范围这么小,直接打表就行了

打表程序

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
unordered_map<ll,pair<int,int> > mp;
struct Node
{
    int a,b,c;
} ans[210];

int main()
{
    freopen("out.txt","w",stdout);
    for(int a=-5000; a<=5000; ++a)
        for(int b=-5000; b<=5000; ++b)
        {
            ll res=1ll*a*a*a+1ll*b*b*b;
            mp[res]= {a,b};
        }
    memset(ans,-1,sizeof(ans));
    for(int x=0; x<=200; ++x)
    {
        bool ok=0;
        for(int c=-5000; c<=5000; ++c)
        {
            ll res=x-1ll*c*c*c;

            if(mp.count(res))
            {
                int a=mp[res].first,b=mp[res].second;
                ans[x]= {a,b,c};
                ok=1;
                break;
            }
        }
        if(!ok) ans[x]= {-1,-1,-1};
    }
    printf("{");
    for(int i=0; i<=200; ++i)
        printf("%d,%d,%d,",ans[i].a,ans[i].b,ans[i].c);
    printf("-1};");

    return 0;
}

M. Kill the tree (树的重心)

题意:给定一棵 n 个节点的以 1 为根的树。对以 i ∈ ( 1 , n ) i \in(1,n) i(1,n) 为根的每棵子树找这样的点,其余所有点到该点的距离之和最短(可能有多个) ( 1 ≤ n ≤ 2 × 1 0 5 ) (1\le n\le 2\times 10^5) (1n2×105)

思路:这样的点显然就是重心,然后就是以每个点为根,找子树内的重心。

  • 可以这样想,从叶节点开始不断往上走,重心也会不断往上跑。
  • 假设当前点为 u ,那么取 u 的重儿子 son[u] 的重心 now,然后将 now 和 f a [ n o w ] fa[now] fa[now] 进行比较,如果以 fa[now] 为重心的 sz 更小,那么就往上走,直到走不动为止。
  • 此时比较一下 now 和 son[now] ,如果两个得到的 sz 是一样的,那么就表示这两个都是重心
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;
int n,a,b;
vector<int> e[maxn];
int fa[maxn],sz[maxn],son[maxn];
int ans[maxn][2];
void dfs1(int u)
{
    sz[u]=1;
    for(auto v: e[u])
    {
        if(v==fa[u]) continue;
        fa[v]=u;
        dfs1(v);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u)
{
    for(auto v: e[u])
    {
        if(v==fa[u]) continue;
        dfs2(v);
    }
    int now=ans[son[u]][0];
    if(now==0) now=u;
    while(now!=u&&max(sz[son[now]],sz[u]-sz[now])>=max(sz[son[fa[now]]],sz[u]-sz[fa[now]]))
        now=fa[now];
    ans[u][0]=now;
    if(max(sz[son[now]],sz[u]-sz[now])==max(sz[son[son[now]]],sz[u]-sz[son[now]]))
        ans[u][1]=son[now];
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n-1; ++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(1);
    dfs2(1);
    for(int i=1; i<=n; ++i)
    {
        int a=ans[i][0],b=ans[i][1];
        if(b!=0)
        {
            if(a>b) swap(a,b);
            printf("%d %d\n",a,b);
        }
        else printf("%d\n",a);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值