ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOI Online爆蛋记

2020-03-08 22:55:56  阅读:257  来源: 互联网

标签:NOI int res define Online 爆蛋记 first reg lld


太菜了只能参加TG组

隔壁PJ考的比TG难是怎么肥事??

爆蛋过程

8:20起床

然后一边等题目加载一边吔早饭,ccf这什么破服务器

8:40才看到题面,先把ABC都看了一遍

先把A50分打了,然后去想B (9:00)

B推了半天推出了交换相邻最多只会影响逆序对数1个的结论,但还是没想出40分做法,又回去看A

然后发现A的60分判个奇偶性就行了(不知道对不对),然后一会就打完了,(9:30

然后又去推B,瞎推竟然推出来一个结论:(9:50

定义 \(s_i\) 为第 \(i\) 个数前比第 \(i\) 个数大的数,那么每一次操作都会把所有 \(s_i\) 往前移动1位并且都减去1,0不用减

那么就是要查询一段区间内大于某个数的数字的和以及个数

我当时想这是个动态二维数点问题,然后不断尝试写二维BIT啥的

在10:50的时候我突然想到,\(s_1\) 一定是0,那么就变成了查询整个数组内大于某个数的数字和以及个数

权!值!线!段!树!

11:00过了样例,11:10完成对拍

然后C题对于每个k就相当于n个数组成几个大小相同的环,枚举因数即可,暴力模拟答案

在11:59:43调完交了上去

不过有个地方写炸了,所以可能爆蛋/kk

期望得分:60+100+100=260

听说C班除了我都AK了QAQ/kk %%%%%%%%%%%%%%%%%%%%%%%%

题解

A

对于 1~5,直接判断 \(a_1-b_1\) 是否等于 \(a_2-b_2\) 即可

对于 6~10,判断 \(a_1-b_1=b_2-a_2\) 或者 \(a_2-b_2=b_1-a_1\) ,两个满足一个即可

对于 11~12,如果 \(t=1\) 和 \(t=2\) 的操作只有其中一个那么直接按照 1~10 里的做法做

否则判断一下奇偶性即可,判断 \((a_1-b_1) \bmod 2 = (a_2-b_2) \bmod 2\) 即可

剩下不会了,预计得分 60

// This code wrote by chtholly_micromaker(MicroMaker)
#include <bits/stdc++.h>
#define reg register
#define int long long
using namespace std;
const int MaxN=100050;
struct Quetion
{
    int t,u,v;
}Q[MaxN];
template <class t> inline void read(t &s)
{
    s=0;
    reg int f=1;
    reg char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(isdigit(c))
        s=(s<<3)+(s<<1)+(c^48),c=getchar();
    s*=f;
    return;
}
int n,m;
int a[MaxN],b[MaxN];
namespace SUBTASK1
{
    inline void solve()
    {
        if(a[1]-b[1]==a[2]-b[2])
            puts("YES");
        else
            puts("NO");
        return;
    }
}
namespace SUBTASK2
{
    inline void solve()
    {
        if((a[1]-b[1]==b[2]-a[2])||(a[2]-b[2]==b[1]-a[1]))
            puts("YES");
        else
            puts("NO");
        return;
    }
}
namespace SUBTASK3
{
    inline void solve(int c1,int c2)
    {
        if(c1&&c2)
        {
            if(((a[1]-b[1])&1)==((a[2]-b[2])&1))
                puts("YES");
            else
                puts("NO");
            return;
        }
        else
        {
            if(c1)
                SUBTASK1::solve();
            else
                SUBTASK2::solve();
        }
        return;
    }
}
inline void work()
{
    read(n);read(m);
    for(int i=1;i<=n;++i)
        read(a[i]);
    for(int i=1;i<=n;++i)
        read(b[i]);
    reg int cnt1=0,cnt2=0;
    for(int i=1;i<=m;++i)
    {
        read(Q[i].t);read(Q[i].u);read(Q[i].v);
        if(Q[i].t==2)
            ++cnt2;
        if(Q[i].t==1)
            ++cnt1;
    }
    if(n==2&&m==1&&cnt1)
    {
        SUBTASK1::solve();
        return;
    }
    if(n==2&&m==1&&cnt2)
    {
        SUBTASK2::solve();
        return;
    }
    if(n==2)
    {
        SUBTASK3::solve(cnt1,cnt2);
        return;
    }
    return;
}
signed main(void)
{

    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);

    int t;cin>>t;
    while(t--)
        work();
    return 0;
}

B

我们设原序列为 \(a_i\),\(s_i\) 为 \(a_1,a_2,\cdots,a_{i-1}\) 中大于 \(a_i\) 的数的个数

对于一个 \(a\) 序列求 \(s\) 序列可以用树状数组 \(O(n\log n)\) 实现

我们来看一个例子(假设 \(s_i\) 已经求好了)

a: 1 4 3 5 2
s: 0 0 1 0 3

一轮冒泡后

a: 1 3 4 2 5
s: 0 0 0 2 0

一会发现每个大于 0 的 \(s_i\) 都减去了 1 并且所有 \(s_i\) 都往左移了一位

然后你会发现,\(s_1\) 一定为 0 (由 \(s_i\) 的定义可知)

那么左移操作就可以忽略,那么 \(t=2\) 的操作就变成了

求当前 \(a\) 序列的 \(\sum_{i=1}^n \max(s_i-c,0)\)

那么也就是询问(这里序列指 \(s\) 序列)
\[ \text{序列内所有大于} c \text{的数字的和}-\text{序列内大于} c \text{的数字的个数}\times c \]
这个直接权值线段树维护就行(或者两个树状数组也可以)

现在,如果交换操作直接暴力重新求 \(s\) 序列就能拿到 60 分了

但离 100 分已经不远了

我们还是来观察一下:

假设 \(p<q\)

a: p q
s: x y

交换 \(p,q\) ,因为 \(p<q\) 所以 \(p\) 对应的 \(s\) 要 \(+1\) ,而 \(q\) 对应的 \(s\) 不会变

a: q p
s: y x+1

\(p>q\) 也差不多

所以利用这个性质我们每次交换操作只要单点修改一下 \(s\) 就行,单次修改复杂度 \(O(\log n)\)

总时间复杂度 \(O(m\log n)\)

// This code wrote by chtholly_micromaker(MicroMaker)
#include <bits/stdc++.h>
#define reg register
#define int long long
using namespace std;
const int MaxN=200050;
template <class t> inline void read(t &s)
{
    s=0;
    reg int f=1;
    reg char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(isdigit(c))
        s=(s<<3)+(s<<1)+(c^48),c=getchar();
    s*=f;
    return;
}
int n,m;
int a[MaxN],b[MaxN];
int tr[MaxN];
int val[MaxN<<2];
int sum[MaxN<<2];
inline int lowbit(int x)
{
    return x&(-x);
}
inline void modify(int x,int k)
{
    while(x<=n)
    {
        tr[x]+=k;
        x+=lowbit(x);
    }
    return;
}
inline int queryP(int x)
{
    reg int res=0;
    while(x>0)
    {
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}
inline int query(int l,int r)
{
    return queryP(r)-queryP(l-1);
}
#define lson (u<<1)
#define rson (u<<1|1)
inline void Spushup(int u)
{
    val[u]=val[lson]+val[rson];
    sum[u]=sum[lson]+sum[rson];
    return;
}
inline void Smodify(int u,int l,int r,int p,int k)
{
    if(l==r)
    {
        // printf("Smodify %lld %lldx%lld\n",u,k,l);
        val[u]+=k;
        sum[u]+=k*l;
        return;
    }
    reg int mid=(l+r)>>1;
    if(p<=mid)
        Smodify(lson,l,mid,p,k);
    else
        Smodify(rson,mid+1,r,p,k);
    Spushup(u);
    return;
}
inline pair<int,int> Squery(int u,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr)
        return make_pair(val[u],sum[u]);
    reg int mid=(l+r)>>1;
    reg pair<int,int> res=make_pair(0,0);
    if(ql<=mid)
    {
        reg pair<int,int> t=Squery(lson,l,mid,ql,qr);
        res.first+=t.first;
        res.second+=t.second;
    }
    if(mid<qr)
    {
        reg pair<int,int> t=Squery(rson,mid+1,r,ql,qr);
        res.first+=t.first;
        res.second+=t.second;
    }
    // printf("Squery %lld %lld is %lld %lld\n",l,r,res.first,res.second);
    return res;
}
inline void recalc()
{
    // memset(tr,0,sizeof tr);
    // memset(val,0,sizeof val);
    // memset(sum,0,sizeof sum);
    for(int i=1;i<=n;++i)
    {
        b[i]=query(a[i],n);
        // printf("recalc %lld is %lld\n",i,t);
        if(b[i])
            Smodify(1,1,n,b[i],1);
        modify(a[i],1);
    }
    return;
}
inline void work(int i,int j)
{
    if(a[i]>a[j])
    {
        if(b[j]>0)
            Smodify(1,1,n,b[j],-1);
        --b[j];
        if(b[j]>0)
            Smodify(1,1,n,b[j],1);
        swap(a[i],a[j]);
        swap(b[i],b[j]);
    }
    else
    {
        if(b[i]>0)
            Smodify(1,1,n,b[i],-1);
        ++b[i];
        if(b[i]>0)
            Smodify(1,1,n,b[i],1);
        swap(a[i],a[j]);
        swap(b[i],b[j]);
    }
    return;
}
signed main(void)
{

    freopen("bubble.in","r",stdin);
    freopen("bubble.out","w",stdout);

    cin>>n>>m;
    for(int i=1;i<=n;++i)
        read(a[i]);
    recalc();
    reg int t,c;
    for(int i=1;i<=m;++i)
    {
        read(t);read(c);
        if(t==1)
        {
            // swap(a[c],a[c+1]);
            work(c,c+1);
        }
        else
        {
            if(c>=n)
                puts("0");
            else
            {
                reg pair<int,int> res=Squery(1,1,n,c+1,n);
                // printf("query %lld %lld is %lld %lld\n",c+1,n,res.first,res.second);
                printf("%lld\n",res.second-res.first*c);
            }
        }
    }
    return 0;
}

C

先大力手玩一下,发现环的长度为 \(\frac n {\gcd(n,k)}\)

环长度相同答案一定相同,所以直接枚举因子,预处理

\(\{ 1,2,3,4,5,6 \}\) 在 \(k=1\) 的时候放成 \(\{ 2,4,6,5,3,1 \}\)

可以发现,从大到小来说的话,先放 6 ,6 的右边放 5 ,6 的左边放 4,6的右边的右边放 3,6 的左边的左边放 2

于是很显然,弄两个指针就行

然后 \(k>1\) 的时候就对每个环进行这样的处理即可

记得特别处理一下 \(k=0\) 的情况

// This code wrote by chtholly_micromaker(MicroMaker)
#include <bits/stdc++.h>
#define reg register
#define int long long
using namespace std;
const int MaxN=200050;
template <class t> inline void read(t &s)
{
    s=0;
    reg int f=1;
    reg char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(isdigit(c))
        s=(s<<3)+(s<<1)+(c^48),c=getchar();
    s*=f;
    return;
}
int n;
int a[MaxN],b[MaxN],ans[MaxN];
int thezero;
inline int dfs(int l,int r)
{
    // printf("dfs %lld %lld\n",l,r);
    reg int L=1e5;
    reg int R=1e5-1;
    for(int i=r;i>=l;--i)
        if(i&1)
            b[++R]=a[i];
        else
            b[--L]=a[i];
    reg int res=0;
    for(int i=L;i<=R;++i)
        if(i==R)
            res+=b[R]*b[L];
        else
            res+=b[i]*b[i+1];
    // puts("ok");
    return res;
}
inline int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
signed main(void)
{

    // freopen("ring.in","r",stdin);
    // freopen("ring.out","w",stdout);

    int m;
    cin>>n>>m;
    for(int i=1;i<=n;++i)
        read(a[i]),thezero+=a[i]*a[i];
    sort(a+1,a+n+1);
    for(int i=1;i<n;++i)
        if(!(n%i))
        {
            for(int j=1;j<=n;j+=n/i)
                ans[i]+=dfs(j,j+n/i-1);
        }
    for(int i=1;i<=m;++i)
    {
        reg int c;
        read(c);
        if(!c)
            printf("%lld\n",thezero);
        else
            printf("%lld\n",ans[gcd(c,n)]);
    }
    return 0;
}

标签:NOI,int,res,define,Online,爆蛋记,first,reg,lld
来源: https://www.cnblogs.com/chinesepikaync/p/12445637.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有