[NOIP2018模拟赛10.23]发呆报告

闲扯

考场看了眼题目感觉很难,一个小时敲完了所有暴力...嗯然后就在那里发呆什么事也没做

T3考场上把数据结构想了个遍都不会完成1操作,现在看这种思路其实之前也接触过...

比较玄学的一件事情就是T1一开始测得有40分结果过了会看爆0了,难不成被续走了(然而后面测了一下真的爆0了)

太菜了不讲了

T1 sequence

首先通过大佬博客了解一下\(k\)阶前缀和: https://blog.csdn.net/hrbust_cx/article/details/82431567

通过看那张图你会发现这个\(k\)阶前缀和也真神奇, 仔细观察\(a[3]\)那行,我们我们规定列编号从\(0\)开始,那么\(a[3][0]= C^3_3 = 1\) \(a[3][1] = C^4_3\) \(a[3][2] =C^5_3\) \(a[3][3]= C^6_3\)

也就是说如果你在第\(p\)阶使\(a[p][0]+1\),那么前缀和后\(a[p+k][i] = C^{k+i}_k\)

再看看我们要维护的式子,于是就可以差分开搞了

但是发现我们从\(p\)\(p+k\)每一(阶)行在\(r+1\)处都需要差分一下,这个可以发现就是一个组合数,可以根据上面结论,也可以根据它斜着看就是个杨辉三角的性质

我们只要预先处理好\(C^i_j (1<=i<=n+k, 0<=j<=k)\)就可以\(O((n+m)k)\)解决这道题

话说又是CF题: https://www.luogu.org/problemnew/show/CF407C

/*
  code by RyeCatcher
*/
const int maxn=500035;
const int maxc=22;
const int inf=0x7fffffff;
const ll INF=1e17+19260817;
const ll P=1e9+7;
int n,m;
ll f[maxn][maxc],c[maxn][maxc],s[maxn][maxc];
int a[maxn];
int main(){
    int x,y,l,r,k;
    //FO(sequence);
    read(n),read(m);
    for(ri i=0;i<=n+22;i++){
        c[i][0]=1;
        for(ri j=1;j<=20;j++){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
        }
    }
    while(m--){
        read(l),read(r),read(k);
        f[l][k]++;
        for(ri i=0;i<=k;i++)f[r+1][k-i]=(P+(f[r+1][k-i]-c[i+r-l][i])%P)%P;//计算右边的差分项
    }
    for(ri i=20;i>=0;i--){
        for(ri j=1;j<=n;j++)
            f[j][i]=(f[j][i]+f[j-1][i]+f[j][i+1])%P;//等价于上一阶的前缀和,也是种计算的方法
    }
    for(ri i=1;i<=n;i++)printf("%lld ",f[i][0]);
    return 0;
}

T2 bomb

讲题人: 这是一个经典的DP状态转移

我: ???

有一个部分分,就是求\(n\)个节点的带标号无向联通图个数.设\(f[i]\)\(i\)个节点的带标号无向连通图个数,\(g[i]\)表示\(i\)个节点能构成带标号图的数量,易知\(g[i]= 2 ^{ C^2 _i } = 2 ^{ i \times (i-1) /2}\)

正难则反(我现在还记得卫中说的这句话),我们用\(i\)个节点能构成图的个数减去不连通的情况

\(f[i] =g[i] - \sum^{i-1}_{j=1} g[i-j] \times C^{i-1}_{j-1} \times f[j]\)

我们相当于枚举有\(j\)个点是连通的,剩下\(i-j\)个不连通与其不连通,这个不连通的图有\(g[i-j]\)

由于是有标号的,我们钦定1号点在联通的块中,那么剩下构成联通的图的点数标号集合就有\(C^{i-1}_{j-1}\)种可能,联通图的形态也有\(f[j]\)种可能

再类比于之前一道模拟赛题将恰好为\(k\)转化为小于等于\(k\)的减去小于等于\(k-1\)的.在这题我们发现这样转化会更好处理

\(ff[i]\)表示\(i\)个节点,且联通块大小小于等于k的图的个数

\(ff[i] = \sum^{min(i,k)}_{j=1} C^{i-1}_{j-1} \times f[j] \times ff[i-j]\)

我们类似的分成两个部分乘法原理就好了

/*
  code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::min;
using std::max;
using std::priority_queue;
using std::queue;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
    while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=2005;
const ll inf=1e17+19260817;
const ll P=998244353;
ll f[maxn],ff[maxn],fff[maxn],g[maxn],c[maxn][maxn];
int n,k;
ll ksm(ll p,int c){
    ll ans=1;
    while(c){
        if(c&1)ans=ans*p%P;
        p=p*p%P;
        c=c>>1;
    }
    return ans%P;
}
int main(){
    FO(bomb);
    read(n),read(k);
    for(ri i=0;i<=n;i++){
        c[i][0]=1,g[i]=ksm(2,i*(i-1)/2);//printf("%d %d %lld\n",i,i*(i-1)/2,g[i]);
        for(ri j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
    }
    for(ri i=1;i<=n;i++){
        f[i]=g[i];
        for(ri j=1;j<i;j++){
            f[i]=(f[i]-(g[i-j]*c[i-1][j-1]%P*f[j])%P+P)%P;
        }
        //printf("%d %lld %lld\n",i,f[i],g[i]);
    }
    ff[0]=fff[0]=1;
    for(ri i=1;i<=n;i++){
        ff[i]=fff[i]=0;
        for(ri j=1;j<=min(i,k);j++)ff[i]=(ff[i]+c[i-1][j-1]*f[j]%P*ff[i-j])%P;
        for(ri j=1;j<=min(i,k-1);j++)fff[i]=(fff[i]+c[i-1][j-1]*f[j]%P*fff[i-j])%P;
    }
    printf("%lld\n",((ff[n]-fff[n])%P+P)%P);
    return 0;
}

T3 queue

考虑使用分块在线处理,我们发现瓶颈是1操作,我们将在\([l,r]\)上的这个操作看成将r放到l位之前,然后所有都往后一位

于是我们可以将每一块设立两个指针指向块的左右端点,对于同一块内的操作暴力处理

对于\(l,r\)在不同块上的情况,一种naiive的做法是直接将\(r\)位的元素插入到\(l\)之前,但是这样可能会使块的大小不平衡,需要分裂重构比较麻烦

但是有个方法能避免这个问题

\(l\)所在块为p,\(r\)所在块为q,我们把\(p\)\(q-1\)中的每一块的末尾元素放到下一块的开头,这样的话就保持平衡了

我们可以将每一块设成一个\(deque\)(双端队列)来简洁地完成上述操作

/*
  code by RyeCatcher
*/
const int maxn=100005;
const int maxb=355;
const int inf=0x7fffffff;
int n,m,size,k,a[maxn];
int L[maxn],R[maxn],pos[maxn];
deque <int> blo[maxb];
int cnt[maxn][maxb],sz[maxn];
int main(){
    int x,y,l,r;
    FO(queue);
    freopen("queue2.in","r",stdin);
    freopen("queue2.ans","w",stdout);
    //DEBUG
    //freopen("dat.in","r",stdin);
    read(n),read(m);
    if(!(n+m)){
        return 0;
    }
    size=sqrt(n+0.5);
    for(ri i=1;i<=size;i++)L[i]=(i-1)*size+1,R[i]=i*size;
    if(R[size]<n)R[++size]=n,L[size]=R[size-1]+1;
    for(ri i=1;i<=n;i++){
        read(a[i]);
    }
    for(ri i=1;i<=size;i++){
        for(ri j=L[i];j<=R[i];j++){
            pos[j]=i;
            sz[i]++;
            cnt[a[j]][i]++;
            blo[i].push_back(a[j]);
        }
    }
    int opt,p,q;
    deque<int>::iterator it;
    while(m--){
        read(opt),read(l),read(r);
        p=pos[l],q=pos[r];
        if(opt==1){
            if(p==q){
                x= *(blo[p].begin()+r-L[p]);
                blo[p].erase(blo[p].begin()+r-L[p]);
                blo[p].insert(blo[p].begin()+l-L[p],x);
            }
            else{
                x= *(blo[q].begin()+r-L[q]);
                //printf("--%d %d--\n",q,x);
                blo[q].erase(blo[q].begin()+r-L[q]);
                sz[q]--,cnt[x][q]--;
                for(ri i=p;i<q;i++){
                    y= blo[i].back();
                    blo[i].pop_back();
                    cnt[y][i]--,sz[i]--,
                    blo[i+1].push_front(y);
                    cnt[y][i+1]++,sz[i+1]++;
                }
                blo[p].insert(blo[p].begin()+l-L[p],x);
                sz[p]++,cnt[x][p]++;
            }
        }
        if(opt==2){
            read(k);
            if(p==q){
                x=0;
                for(it=blo[p].begin()+l-L[p];it<=blo[p].begin()+r-L[p];it++){
                    if(*it==k)x++;
                }
                printf("%d\n",x);
            }
            else{
                x=0;
                for(it =blo[p].begin()+l-L[p];it!=blo[p].end();it++){
                    if(*it==k)x++;
                    //printf("%d ",*it);
                }
                for(it =blo[q].begin();it<=blo[q].begin()+r-L[q];it++){
                    if(*it==k)x++;
                    //printf("%d ",*it);
                }
                for(ri i=p+1;i<q;i++)x+=cnt[k][i];
                printf("%d\n",x);
            }
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/Rye-Catcher/p/9839086.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值