首页 > 代码库 > FR #10题解

FR #10题解

好 蠢 啊

A.

  标准分治。每次从分治区间中找到最大值的位置m,设f[l,r]为[l,r]的答案,那么f[l,r]=f[l,m-1]+f[m+1,r]+跨过m点的贡献。

     然后枚举小的区间放到大的区间中查就行了。复杂度nlog^2n。

     TM的这5e5你给128M怎么回事。。。开6s又怎么回事。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 500050
#define maxq 10000050
using namespace std;
int n,a[maxn],b[maxn],hash[maxn],tot=0,ls[maxq/10+1],rs[maxq/10+1],root,tot_pnt=0,cnt=0,kr=0;
int p;
struct seg_tr
{
    int mx,pos;
    seg_tr (int mx,int pos):mx(mx),pos(pos) {}
    seg_tr () {}
}s[maxq/10+1];
struct query
{
    int pos,lim;
}q1[maxq>>1],q2[maxq>>1];
int numq1=0,numq2=0,t[maxn];
long long ans=0;
bool cmp(query x,query y)
{
    return x.pos<y.pos;
}
int lowbit(int x) {return (x&(-x));}
int read()
{
    char ch;int data=http://www.mamicode.com/0;
    while (ch<0 || ch>9) ch=getchar();
    while (ch>=0 && ch<=9)
    {
        data=data*10+ch-0;
        ch=getchar();
    }
    return data;
}
void addq(int nows,int l,int r,int val)
{
    if (l-1)
    {
        ++numq1;
        q1[numq1].pos=l-1;
        q1[numq1].lim=lower_bound(hash+1,hash+tot+1,val/a[nows])-hash;
        if (hash[q1[numq1].lim]!=(val/a[nows])) q1[numq1].lim--;
    }
    if (r)
    {
        ++numq2;
        q2[numq2].pos=r;
        q2[numq2].lim=lower_bound(hash+1,hash+tot+1,val/a[nows])-hash;
        if (hash[q2[numq2].lim]!=(val/a[nows])) q2[numq2].lim--;
    }
}
seg_tr combine(seg_tr x,seg_tr y)
{
    if (x.mx>y.mx) return x;
    else return y;
}
void build(int &now,int left,int right)
{
    now=++tot_pnt;
    if (left==right) {s[now]=seg_tr(a[left],left);return;}    
    int mid=(left+right)>>1;
    build(ls[now],left,mid);build(rs[now],mid+1,right);
    s[now]=combine(s[ls[now]],s[rs[now]]);
}
seg_tr ask(int now,int left,int right,int l,int r)
{
    if ((left==l) && (right==r)) return s[now];
    int mid=(left+right)>>1;
    if (r<=mid) return ask(ls[now],left,mid,l,r);
    else if (l>=mid+1) return ask(rs[now],mid+1,right,l,r);
    else return combine(ask(ls[now],left,mid,l,mid),ask(rs[now],mid+1,right,mid+1,r));
}
void conc1(int left,int right)
{
    if (left>right) return;
    int pos=ask(root,1,n,left,right).pos;
    if (pos-left+1<=right-pos+1)
        for (int i=left;i<=pos;i++)
            addq(i,pos,right,a[pos]);
    else
        for (int i=pos;i<=right;i++)
            addq(i,left,pos,a[pos]);
    conc1(left,pos-1);conc1(pos+1,right);
}
void add(int pos,int val)
{
    for (int i=pos;i<=tot;i+=lowbit(i))
        t[i]+=val;
}
int ask(int pos)
{
    int ret=0;
    for (int i=pos;i>=1;i-=lowbit(i))
        ret+=t[i];
    return ret;
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++)
    {
        a[i]=read();b[i]=a[i];if (a[i]==1) cnt++;
        hash[++tot]=a[i];
    }
    sort(hash+1,hash+tot+1);tot=unique(hash+1,hash+tot+1)-hash-1;
    for (int i=1;i<=n;i++) b[i]=lower_bound(hash+1,hash+tot+1,a[i])-hash;
    build(root,1,n);
    conc1(1,n);
    sort(q1+1,q1+numq1+1,cmp);sort(q2+1,q2+numq2+1,cmp);
    p=1;
    for (int i=1;i<=n;i++)
    {
        add(b[i],1);
        while (q1[p].pos==i) 
        {
            ans-=ask(q1[p].lim);
            p++;
        }
    }
    p=1;memset(t,0,sizeof(t));
    for (int i=1;i<=n;i++)
    {
         add(b[i],1);
        while (q2[p].pos==i)
        {
            ans+=ask(q2[p].lim);
            p++;
        }
    }
    printf("%lld\n",ans-cnt);
    return 0;
}

B.

  打表发现,变换2^k次之后数组为b,那么b[i]=a[i]^a[i+2^k(从1..n旋转的意义下)]。

    然后按照二进制按位算就行了。

    考试的时候手推到第四次变换觉得一点规律没有。。然后瞬间觉得这是一个组合问题。。。然后又想到lucas。。。越来越远了。。。

    二进制分组是个很重要的想法。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100500
using namespace std;
long long n,m,a[maxn],b[maxn],tab[70];
long long read()
{
    char ch;long long data=http://www.mamicode.com/0;
    while (ch<0 || ch>9) ch=getchar();
    while (ch>=0 && ch<=9)
    {
        data=data*10+ch-0;
        ch=getchar();
    }
    return data;
}
long long trans(long long x)
{
    if (x<=n) return x;
    return (x-1)%n+1;
}
int main()
{
    n=read();m=read();m--;
    for (long long i=1;i<=n;i++) a[i]=read();
    tab[0]=1;for (long long i=1;i<=63;i++) tab[i]=tab[i-1]<<1;
    long long ret=0;
    while (m)
    {
        if (m&1)
        {
            for (long long i=1;i<=n;i++) b[i]=a[i]^a[trans(i+tab[ret])];
            for (long long i=1;i<=n;i++) a[i]=b[i];
        }
        m>>=1;ret++;
    }
    for (long long i=1;i<=n;i++) printf("%lld ",a[i]);
    printf("\n");
    return 0;
}

C.

  首先得到原式=Σ(i=1..√n)Σ(j=i+1..n/i) [gcd(i,j)=1]。

     然后变成两段区间,直接2^质因子个数 容斥。

     就完了

     就完了

     复杂度√nlogn。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100500
using namespace std;
long long n,prime[maxn],tot=0,w[maxn][60],ans=0,mn[maxn],f,kr=0;
bool vis[maxn];
void get_table()
{
    for (long long i=2;i<=maxn-500;i++)
    {
        if (!vis[i]) prime[++tot]=mn[i]=i;
        for (long long j=1;i*prime[j]<=maxn-500;j++)
        {
            vis[i*prime[j]]=true;mn[i*prime[j]]=prime[j];
            if (i%prime[j]) continue;break;
        }
    }
    for (long long i=2;i<=maxn-500;i++)
    {
        long long ret=-1,x=i;
        while (x!=1)
        {
            if (mn[x]!=ret) {ret=mn[x];w[i][0]++;w[i][w[i][0]]=mn[x];}
            x/=mn[x];
        }
    }
}
void dfs(long long now,long long top,long long ret,long long val,long long n)
{
    if (now==w[top][0]+1)
    {
        if (!ret) return;
        if (ret&1) kr+=n/val;else kr-=n/val;
        return;
    }
    dfs(now+1,top,ret,val,n);
    dfs(now+1,top,ret+1,val*w[top][now],n);
}
void ask(long long n,long long val) {kr=0;dfs(1,val,0,1,n);}
int main()
{
    scanf("%lld",&n);
    get_table();
    long long top=sqrt(n);
    for (long long i=2;i<=top;i++)
    {
        ask(n/i,i);ans+=(n/i)-kr;
        ask(i,i);ans-=i-kr;
    }
    printf("%lld\n",ans);
    return 0;
}

 

FR #10题解