ABC-254
D-Together Square(数学)
Problem
给定\(N\),求\(1-N\)中有多少个数对\((i,j)\)的乘积是完全平方数
\(1\le N\le 2\times 10^5\)
Solve
考虑一个数\(x=p_1^{a_1}p_2^{a_2}...p_n^{a_n}\)(\(p_i\in primes\)),如果\(x\)是完全平方数,那么每个\(a_i\)都要是偶数。现在令\(f(x)\)表示\(x\)的最大的平方数因子,那么\(i\times j\)是完全平方数当且仅当\(\frac{i\times j}{f(i)\times f(j)}\)是完全平方数。
考虑证明:设\(i=p_1^{a_1}p_2^{a_2}...p_n^{a_n}\),对于每个素因子,如果\(a_i\)是偶数,那么\(f(i)\)的\(p_i\)的幂次就是\(a_i\),否则就是\(a_i-1\)。因此\(f(i)=p_1^{a_i^{'}}...p_n^{a_n^{'}}\),其中\(a_i-1\le a_i^{'}\le a_i\)。因此\(\frac{i}{f(i)}\)的算术分解的素数的幂次不会超过\(1\)。那么\(i\times j\)如果是完全平方数,那么它们的算术分解就不可能有奇数次幂,因此对于\(\frac{i}{f(i)}=\frac{j}{f(j)}\),很好理解,因为对于一个素因子来说,如果\(\frac{i}{f(i)}\)有,那么只能包含\(1\)次幂,那么要想乘积有偶数次幂,显然\(\frac{j}{f(j)}\)也要有\(1\)次幂。如果某个素数其中一个不包含,那么两个都不会包含。因此就是统计有多少\(\frac{i}{f(i)}=\frac{j}{f(j)}\)
Code
#include <bits/stdc++.h>
using namespace std;
priority_queue<int>q[200005];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
vector<int>f(n+1);
vector<bool>st(n+1);
for(int i=1;i*i<=n;i++) st[i*i]=1;
vector<vector<int>>yz(n+1);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i) yz[j].push_back(i);
vector<int>cnt(n+1);
for(int i=1;i<=n;i++)
{
int f=0;
for(auto x:yz[i])
if(st[x]) f=x;
cnt[i/f]++;
}
int ans=0;
for(int i=1;i<=n;i++)
ans+=cnt[i]*cnt[i];
cout<<ans<<'\n';
}
F-Rectangle GCD(区间 gcd、线段树、RMQ)
Problem
有一个长度为\(N\)的正整数序列\(A\)和长度为\(N\)的正整数序列\(B\),现在定义一个\(N\times N\)的矩阵的元素\((i,j)\)是\(A_i+B_j\)。现在有\(Q\)个询问,每次每次给定\(x_1,y_1,x_2,y_2\),询问左上角是\((x_1,y_1)\),右下角是\((x_2,y_2)\)的子矩阵中元素的\(GCD\)是多少
Solve
对于同一列来说\(gcd(A_i+B_j,A_{i+1}+B_j)=gcd(A_i+B_j,A_{i+1}-A_i)\),假设当前查询\(h1\)到\(h2\)之间的区域,那么\(gcd(A_{h_1}+B_j,A_{h_1+1}+B_j,...,A_{h_2}+B_j)=gcd(A_{h_1}+B_j,A_{h_1+1}-A_{h_1},...,A_{h_2}-A_{h_2-1})\),同理假设当前查询\(w_1\)到\(w_2\)之间的区域,那么\(gcd(B_{w_1}+A_j,B_{w_1+1}+A_j,...,B_{w_2}+A_j)=gcd(B_{w_1}+A_j,B_{w_1+1}-B_{w_1},...,B_{w_2}-B_{w_2-1})\)。于是如果我们求\((h_1,w_1)\)到\((h_2,w_2)\)之间的区域,我们只需要求前面那两部分的\(gcd\)和\(A_{h_1}+B_{w_1}\)的 gcd 即可。求区间\(gcd\)用线段树或 RMQ 都可以。
Code
#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N=2e5+10;
int a[N],b[N];
struct Segment_tree{
int tr[N*4];
void build(int rt,int l,int r,int a[])
{
if(l==r)
{
tr[rt]=abs(a[l]-a[l-1]);
return;
}
int mid=l+r>>1;
build(ls,l,mid,a);
build(rs,mid+1,r,a);
tr[rt]=__gcd(tr[ls],tr[rs]);
}
int query(int rt,int L,int R,int l,int r)
{
if(l<=L&&R<=r) return tr[rt];
int mid=L+R>>1;
if(r<=mid) return query(ls,L,mid,l,r);
else if(l>mid) return query(rs,mid+1,R,l,r);
else return __gcd(query(ls,L,mid,l,mid),query(rs,mid+1,R,mid+1,r));
}
}sega,segb;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
sega.build(1,1,n,a);
segb.build(1,1,n,b);
while(q--)
{
int h1,h2,w1,w2;
cin>>h1>>h2>>w1>>w2;
int ans=a[h1]+b[w1];
if(h1<h2) ans=__gcd(ans,sega.query(1,1,n,h1+1,h2));
if(w1<w2) ans=__gcd(ans,segb.query(1,1,n,w1+1,w2));
cout<<ans<<'\n';
}
return 0;
}
G-Elevators(DP 优化、二进制优化)
Problem
有\(N\)座摩天大楼,每座都有\(10^9\)层楼。不同楼的同一层可以通过天桥互相到达,只需要花费\(1min\)。同时,这\(N\)座摩天大楼里面总共有\(M\)个电梯,一个电梯用\(A_i,B_i,C_i\)来描述,表示第\(A_i\)座摩天大楼有一个\(B_i\)到\(C_i\)层之间的电梯,利用电梯的花费是你所在的层数\(x\)到你要到达的层数\(y\)的差值的绝对值,即\(|x-y|\)。现在有\(Q\)个询问,每次询问给定\(X_i,Y_i,Z_i,W_i\),要求从第\(X_i\)座摩天大楼的第\(Y_i\)层到第\(Z_i\)座摩天大楼的\(W_i\)层所需要花费的最少时间
\(1\le N,M\le 2\times 10^5\)
Solve
一个很显然的事情,就是假设我们\(Y_i\le W_i\),那么我们从\(Y_i\)到\(W_i\)的花费是固定的就是\(W_i-Y_i\),因此问题就是需要使用最少次数的天桥。
待补
Ex-Multiply or Divide by 2
Problem
给定两个长度为\(N\)的非负整数序列\(A\)和\(B\),你可以进行以下操作任意多次:
- 选择一个在\(A\)中的数\(x\)。删掉\(A\)中的一个\(x\),然后替换成成\(2x\)
- 选择一个在\(A\)中的数\(x\)。删掉\(A\)中的一个\(x\),然后替换成成\(\lfloor \frac{x}{2} \rfloor\)
Solve
操作 2 可以看做从序列\(A\)中把一个数二进制下最后一位删掉
操作 1 可以看做从序列\(B\)中选出一个二进制下最后一位为\(0\)的数删掉最后一位
把每个数插入一个 Trie 树中,并且记录每个节点包含的\(A\)和\(B\)中的数的个数
考虑从叶子节点往上搜,并且优先往\(0\)走,对于一个叶子节点\(p\),由于最后两个序列要一样,所以我们肯定考虑把多的变成少的,就是假设当前节点\(A\)中有\(x\)个数,\(B\)中有\(y\)个数,假设\(x>y\),那么我们肯定要操作\(x-y\)个数,使得这一层\(A,B\)所含数的个数相同,然后\(A\)中多的那一些数由于一次操作就变成\(fa[p]\)这一层中\(A\)中的数了。假设走到了\(1\)的这个节点,并且下面已经搜完了,由于\(B\)只能删掉某尾的\(0\),而这下面的要么是已经匹配好的,无法匹配的要么就传递到当前节点了,如果当前节点的右儿子还有\(B\)的数,那么这个数是不可能传递上来的,所以就无解了。
Code
#include <bits/stdc++.h>
using namespace std;
const int N=8000005;
int tr[N][2],ca[N],cb[N];
int n;
int idx;
int ans;
void insert(int x,int c[])
{
int p=0;
for(int i=x?__lg(x):-1;~i;i--)
{
int o=(x>>i)&1;
if(!tr[p][o]) tr[p][o]=++idx;
p=tr[p][o];
}
c[p]++;
}
void dfs(int x)
{
if(tr[x][0])
{
dfs(tr[x][0]);
ca[x]+=ca[tr[x][0]],cb[x]+=cb[tr[x][0]];
}
if(tr[x][1])
{
dfs(tr[x][1]);
ca[x]+=ca[tr[x][1]];
if(cb[tr[x][1]])
{
cout<<"-1\n";
exit(0);
}
}
int y=min(ca[x],cb[x]);
ca[x]-=y,cb[x]-=y;
ans+=ca[x]+cb[x];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
insert(x,ca);
}
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
insert(x,cb);
}
dfs(0);
cout<<ans<<'\n';
return 0;
}