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)⋯⊕(r−1)⊕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}) (1≤X,Y,ai≤1018)
思路:其实就是使下面这个式子能够整除。
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 0≤∣a∣,∣b∣,∣c∣≤5000
思路: 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) (1≤n≤2×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;
}