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=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=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;
}
时间: 2024-12-14 18:46:09

FR #10题解的相关文章

FR #11题解

A. 瞎jb折半dp一下,然后合并一下就好了. O2加成十分显著...6s变成0.9s... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> #define maxv 100 #define maxe 20000 using namespace std; int n,m,d,x,y,z,g[maxv],nume=1

FR #2题解

A. 考虑把(u,v)的询问离线挂在u上,然后dfs,每次从fath[x]到[x]相当于x子树dis区间加1,x子树以外区间-1,然后维护区间和区间平方和等. 常数略大. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define maxv 100500 #define maxe 200500 using na

剑指offer(1~10)题解

剑指offer(1~10) 二维数组中的查找 源代码 class Solution { public: bool Find(int target, vector<vector<int> > array) { for(int i = 0 ; i < array.size() ; i ++){ for( int j = array[i].size() - 1 ; j >= 0 ; j--){ if( array[i][j] == target){ return true;

FR #1题解

A. 建图跑最小费用最大流.分类讨论每种情况如何连边,费用怎么定. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxv 105 #define maxe 100500 #define inf 1000000000 using namespace std; int n,m,a[maxv],b[ma

FR #12题解

A. 我的做法是nmlogn的....直接做m次堆贪心就可以.按理说是能过的... 正解直接是在原dp上搞一搞...可以做到n^2+nlog? 2333 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxn 2050 using namespace std; long long n,m,a[max

省选模拟10 题解

A. 食物链 在拓扑序上dp. B. 选点游戏 要求支持合并两棵树,并同时维护树上最大独立集. 考虑特殊情况,每次只加一个标号最大的点,问题是一个简单的动态dp. 离线出最终树的形态,考虑加入的一个叶子节点,更新该节点到1号节点的轻链上信息就可以了. 其实想到这里,一个动态dp的做法已经很显然了,但是考试时仍然没有想到. 仍然维护出原树的形态. 每次的操作是合并两个联通块,也就是合并一对父子. 实际上只要将子节点处的信息更新到父节点直到父节点同连通块的祖先路径上的轻链就可以了. 因为每条重链的链

10.30 NFLS-NOIP模拟赛 解题报告

总结:今天去了NOIP模拟赛,其实是几道USACO的经典的题目,第一题和最后一题都有思路,第二题是我一开始写了个spfa,写了一半中途发现应该是矩阵乘法,然后没做完,然后就没有然后了!第二题的暴力都没码QAQ 现在我来写解题报告了,有点饿了QAQ.. 第一题 题目 1: 架设电话线 [Jeffrey Wang, 2007] 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务,于 是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线.新的电话线架设 在已有的N(2 <=

Codevs2155连续和题解

题目描述 Description 给定n个数 a1,a2,...,an 定义 f(i,j)=ai+ai+1+ai+2+...+aj?1+aj(1≤i≤j≤n) 求 f(i,j) 的最大值 n≤106;?1000≤ai≤1000(1≤i≤n) 输入描述 Input Description 第一行有1个数,n 第二行有n个数,a1,a2,...,an 输出描述 Output Description 输出只有一行,f(i,j)的最大值 样例输入 Sample Input 7 3 6 -8 9 -12

JLOI2015试题大意及部分题解

================Day1=============== T1:求(b+d√2)n的整数部分对p取模后的值 其中bmod2=1,dmod4=1,b2≤d<(b+1)2,n≤1018 思路: 构造数列an=b?an?1+d?b24?an?2 其中a0=2,a1=b 然后我们求出这个数列的通项公式,得到an=(b+d√2)n+(b?d√2)n 由于b2≤d<(b+1)2,因此b?d√2是个小数,当n足够大时对答案的影响可以忽略不计(n较小时直接暴力) 因此得到(b+d√2)n≈an