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;
}
posted @ 2022-06-05 19:26  Arashimu  阅读(152)  评论(0编辑  收藏  举报