NOI Online爆蛋记

太菜了只能参加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;
}
上一篇:新手学习之查看ORACLE 数据库表空间和数据表的大小


下一篇:快速幂