高一上九月上旬日记

9.1

闲话

  • 上午军训开营仪式,强调了下纪律,和 HS 不一样的地方是食堂水果超市购买的水果和酸奶也不能外带,严禁跑餐(声称会有老师查,以前在 HS 的时候是学校学生会查)。因在站的时间过长,不少班级都有站不住的。
  • 下午军训加拔河比赛、会操比赛。
  • 晚上看《开学第一课》,没让去机房,遂在教室颓《红楼梦》。

做题纪要

9.2

闲话

  • 早上起床后到班里进行肺结核的皮试。
  • 下午进行千人叠被大赛,被迫使用“科技”助力。

做题纪要

牛客 NC278007 Wakey Wakey

  • 手摸一下长度为 \(2\) 的区间发现只有两个数均相等才合法,进一步扩展可知当且仅当 \(a_{1}=a_{2}= \dots =a_{n}\) 才合法。

  • \(m \bmod p\) 即为所求。

    点击查看代码
    int main()
    {
        int t,n,m,p,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>n>>m>>p;
            cout<<m%p<<endl;
        }
        return 0;
    }
    

牛客 NC219949 会赢的!

  • 走到终点需要 \(x+y\) 步,按照其奇偶性判断最终胜者。

  • 如果某人在当前情况下无法取胜,那么在接下来的决策中一定会争取平局,使某一时刻所在点 \((i,j)\) 满足 \(i>x\)\(j>y\) 即可,即当 \(|x-y|>1\) 时平局。

    点击查看代码
    int main()
    {
        int t,x,y,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>x>>y;
            if(x>=0&&y>=0)
            {
                if(abs(x-y)>1)
                {
                    cout<<"PING"<<endl;
                }
                else
                {
                    if((x+y)%2==1)
                    {
                        cout<<"YES"<<endl;
                    }
                    else
                    {
                        cout<<"NO"<<endl;
                    }
                }
            }
            else
            {
                cout<<"PING"<<endl;
            }
        }
        return 0;
    }
    

牛客 NC276193 好好好数

  • \(n\)\(k-\) 好数当且仅当 \(n\)\(k\) 进制表示下各位都 \(\le 1\)

  • \(n\)\(k\) 进制表示下数字最大的那一位即可。

  • 每组询问时间复杂度为 \(O(\log_{k}p)\) ,需要特判 \(k=1\) 的情况。

    点击查看代码
    int main()
    {
        ll t,n,k,ans,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>n>>k;
            ans=1;
            if(k!=1)
            {
                while(n>0)
                {
                    ans=max(ans,n%k);
                    n/=k;
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

牛客 NC277984 好好好数组

  • 由题有 \(a_{i} \in [0,i)\) ,即已固定 \(a_{1}=0\)

  • 手模样例发现至多有 \(3\) 个不同数字。

  • 接着进行大力分讨。

    • 当有 \(1\) 个不同数字时,仅能构造出 \(\forall i \in [1,n],a_{i}=0\) ,故方案数为 \(1\)
    • 当有 \(2\) 个不同数字时,构造出的方案均形如 \(\begin{cases} \forall i \in [1,x),a_{i}=0 \\ \forall i \in [x,n],a_{i}=x \end{cases}\) ,其中 \(x \in [2,n]\) ,故方案数为 \(n-1\)
    • 当有 \(3\) 个不同数字时,仅能构造出 \(\begin{cases} a_{1}=0 \\ \forall i \in [2,n-1],a_{i}=1 \\ a_{n}=n \end{cases}\) ,故方案数为 \(1\)
    点击查看代码
    int main()
    {
        int t,n,m,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>n>>m;
            switch(m)
            {
                case 1:
                    cout<<n+1<<endl;
                    break;
                case 2:
                    cout<<n<<endl;
                    break;
                case 3:
                    cout<<1<<endl;
                    break;
                default:
                    cout<<0<<endl;
                    break;
            }
        }
        return 0;
    }
    

牛客 NC277688 随机化游戏时间?

  • 设区间 \([l,r]\)\(k\) 大为 \(u\) ,第 \(k+1\) 大为 \(v\) ,则幸运数 \(x\) 满足 \(x \in [u,v)\) 。询问时更新 \(x\) 的上下界变化即可。

  • 然后就是静态区间第 \(k\) 大板子。

    点击查看代码
    ll a[100010];
    struct PDS_SMT
    {
        ll root[100010],rt_sum;
        struct SegmentTree
        {
            ll ls,rs,sum;
        }tree[100010<<5];
        #define lson(rt) tree[rt].ls
        #define rson(rt) tree[rt].rs
        void clear()
        {
            rt_sum=0;
        }
        ll build_rt()
        {
            rt_sum++;
            return rt_sum;
        }
        void build_tree(ll &rt,ll l,ll r)
        {
            rt=build_rt();
            if(l==r)
            {
                return;
            }
            ll mid=(l+r)/2;
            build_tree(lson(rt),l,mid);
            build_tree(rson(rt),mid+1,r);
        }
        void update(ll pre,ll &rt,ll l,ll r,ll pos)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            tree[rt].sum++;
            if(l==r)
            {
                return;
            }
            ll mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(pre),lson(rt),l,mid,pos);
            }
            else
            {
                update(rson(pre),rson(rt),mid+1,r,pos);
            }
        }
        ll query(ll rt1,ll rt2,ll l,ll r,ll k)
        {
            if(l==r)
            {
                return l;
            }
            ll mid=(l+r)/2;
            if(k<=tree[lson(rt2)].sum-tree[lson(rt1)].sum)
            {
                return query(lson(rt1),lson(rt2),l,mid,k);
            }
            else
            {
                return query(rson(rt1),rson(rt2),mid+1,r,k-(tree[lson(rt2)].sum-tree[lson(rt1)].sum));
            }
        }
    }T;
    ll qpow(ll a,ll b,ll p)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
            {
                ans=ans*a%p;
            }
            b>>=1;
            a=a*a%p;
        }
        return ans;
    }
    int main()
    {
        ll t,n,m,l,r,k,st,ed,i,j;
        cin>>t;
        for(j=1;j<=t;j++)
        {
            cin>>n>>m;
            T.clear();
            T.build_tree(T.root[0],1,n);
            for(i=1;i<=n;i++)
            {
                cin>>a[i];
                T.update(T.root[i-1],T.root[i],1,n,a[i]);
            }
            st=1;
            ed=n;
            for(i=1;i<=m;i++)
            {
                cin>>l>>r>>k;
                if(k!=0)
                {
                    st=max(st,T.query(T.root[l-1],T.root[r],1,n,k));
                }
                if(k!=r-l+1)
                {
                    ed=min(ed,T.query(T.root[l-1],T.root[r],1,n,k+1)-1);
                }
            }
            if(st==ed)
            {
                cout<<1<<" "<<st<<endl;
            }
            else
            {
                cout<<qpow(ed-st+1,p-2,p)<<endl;
            }
        }
        return 0;
    }
    

9.3

闲话

  • 化奥的回来了。
  • @K8He@jijidawang 的军训被停了,让去机房集训。

做题纪要

牛客 NC278058 随机化游戏时间!

  • 考虑差分约束。

  • \(x_{i}\) 表示 \([1,i]\) 中小于等于幸运数的个数。

  • 建图后跑最短/长路求出上/下界即可。

  • 隐含着 \(0 \le x_{i}-x_{i-1} \le 1\) 的条件。

  • 因为保证有解所以不需要建超级源点。

    点击查看代码
    struct node
    {
        int nxt,to,w;
    }e[400010];
    int head[100010],dis[100010],vis[100010],cnt=0;
    vector<pair<int,int> >E[100010];
    void add(int u,int v,int w)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        e[cnt].w=w;
        head[u]=cnt;
        E[v].push_back(make_pair(u,-w));
    }
    void spfa1(int s)
    {
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        queue<int>q;
        q.push(s);
        dis[s]=0;
        vis[s]=1;
        while(q.empty()==0)
        {
            int x=q.front();
            for(int i=head[x];i!=0;i=e[i].nxt)
            {
                if(dis[e[i].to]>dis[x]+e[i].w)
                {
                    dis[e[i].to]=dis[x]+e[i].w;
                    if(vis[e[i].to]==0)
                    {
                        q.push(e[i].to);
                        vis[e[i].to]=1;
                    }
                }
            }
            vis[x]=0;
            q.pop();
        }
    }
    void spfa2(int s)
    {
        memset(vis,0,sizeof(vis));
        memset(dis,-0x3f,sizeof(dis));
        queue<int>q;
        q.push(s);
        dis[s]=0;
        vis[s]=1;
        while(q.empty()==0)
        {
            int x=q.front();
            for(int i=0;i<E[x].size();i++)
            {
                if(dis[E[x][i].first]<dis[x]+E[x][i].second)
                {
                    dis[E[x][i].first]=dis[x]+E[x][i].second;
                    if(vis[E[x][i].first]==0)
                    {
                        q.push(E[x][i].first);
                        vis[E[x][i].first]=1;
                    }
                }
            }
            vis[x]=0;
            q.pop();
        }
    }
    int main()
    {
        int t,n,m,l,r,k,st,ed,i,j;
        cin>>t;
        for(j=1;j<=t;j++)
        {
            cin>>n>>m;
            cnt=0;
            memset(e,0,sizeof(e));
            memset(head,0,sizeof(head));
            for(i=0;i<=n;i++)
            {
                E[i].clear();
            }
            for(i=1;i<=m;i++)
            {
                cin>>l>>r>>k;
                add(l-1,r,k);
                add(r,l-1,-k);
                
            }
            for(i=1;i<=n;i++)
            {
                add(i-1,i,1);
                add(i,i-1,0);
            }
            spfa1(0);
            ed=dis[n];
            spfa2(0);
            st=dis[n];
            for(i=max(1,st);i<=min(ed,n);i++)
            {
                cout<<i<<" ";
            }
            cout<<endl;
        }
        return 0;
    }
    

CF1515E Phoenix and Computers

  • 考虑预设性 \(DP\)

  • \(f_{i,j}\) 表示已经点亮了 \(i\) 盏灯,其中有 \(j\) 个内部全部点亮的连续段的方案数。边界为 \(f_{1,1}=1\)

  • 对第 \(i\) 个数填入的位置进行分讨。

    • 新开一段
      • \(j-1\) 个连续段共有 \(j\) 个空可以点亮(包括首尾),故有 \(j\) 种方案。此时有 \(f_{i,j}+=f_{i-1,j-1} \times j\)
    • 连接到某个连续段的前后
      • 填入的位置和连续段间没有空位
        • 因为 \(j\) 个连续段前后均可以点亮,故有 \(j\) 种方案。此时有 \(f_{i,j}+=f_{i-1,j} \times 2j\)
      • 填入的位置和连续段间有一个空位
        • 此时这个空位可以被自动点亮,等价于一次性点亮了两盏灯。
        • 分析同上。此时有 \(f_{i,j}+=f_{i-2,j} \times 2j\)
    • 连接两个连续段
      • 两个连续段间有两个空位
        • 两个空位中选出一个手动点亮,剩下一个自动点亮。
        • \(j+1\) 个连续段共有 \(j\) 个空可以点亮(不包括首尾),故有 \(2j\) 种方案。此时有 \(f_{i,j}+=f_{i-2,j+1} \times 2j\)
      • 两个连续段间有三个空位
        • 手动点亮中间的灯,剩下两个自动点亮。
        • \(j+1\) 个连续段共有 \(j\) 个空可以点亮(不包括首尾),故有 \(j\) 种方案。此时有 \(f_{i,j}+=f_{i-3,j+1} \times j\)
  • 最终,有 \(f_{n,1}\) 即为所求。

    点击查看代码
    ll f[410][410];
    int main()
    {
        ll n,p,i,j;
        cin>>n>>p;
        f[1][1]=1;
        for(i=2;i<=n;i++)
        {
            for(j=1;j<=i;j++)
            {
                f[i][j]=(f[i][j]+f[i-1][j-1]*j%p)%p;
                f[i][j]=(f[i][j]+f[i-1][j]*2*j%p)%p;
                f[i][j]=(f[i][j]+f[i-2][j]*2*j%p)%p;
                f[i][j]=(f[i][j]+f[i-2][j+1]*2*j%p)%p;
                if(i-3>=0)
                {
                    f[i][j]=(f[i][j]+f[i-3][j+1]*j%p)%p;
                }
            }
        }
        cout<<f[n][1]<<endl;
        return 0;
    }
    

9.4

闲话

  • 早上团体操过于唐氏。
  • 下午先去进行体检,包括基本内容(身高、体重、肺活量、胸围、腰围、血压),裸眼视力,耳鼻喉,口腔,内科(只问了有没有做过手术)。然后进行学科魅力宣讲,科目为语文、数学、物理、化学、体育、心理。接着进行军训。
  • 晚上因为是师生见面会,分别是英语、物理、化学、生物,每位老师 \(15 \min\) 左右,所以没让去机房。貌似 @K8He@jijidawang 并不知道这个消息。
  • 因为明天有拉歌大赛,班主任被迫去找了首简单易学的红歌《打靶归来》让我们唱,练习时间仅有 \(15 \sim 20 \min\)

做题纪要

9.5

闲话

  • 早上因我和 @xrlong@wang54321 跳得“过于认真”,让我们所处的这一路纵队别跳了,看着别人跳,然后班主任就来救场把我们带回教室上自习了,赢麻了。
  • 上午军训;军训完听校领导的思政会,班主任一针见血地指出那是为了洗脑用的。
  • 下午继续昨天的学科魅力宣讲,科目为英语、生物、政治、历史、地理、艺术;军训时只剩下了三科奥赛,会操时仅有不到 \(5\) 排人;
  • 晚上的国防教育秋季开学第一课被改成了奥赛。

做题纪要

CF704B Ant Man

  • 考虑统计每个数单独的贡献,然后进行预设性 \(DP\)

  • \(f_{i,j}\) 表示当前填了 \([1,i]\) 时有 \(j\) 个连续段的最小权值,边界为 \(f_{0,0}=0\)

  • \(i(i \ne s,i \ne e)\) 填入的位置进行分讨。

    • 新开一段
      • 后面填入的数都比 \(i\) 大(如果存在后面填入的数的话,即 \(j>[i>s]+[i>t]\) ),故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j-1}-2x_{i}+b_{i}+d_{i})\)
    • 连接到某个连续段的前后
      • 连接到某个连续段的前面(如果可以在前面的话,即 \(j>[i>s]\) )时,后面填入的数(在 \(i\) 左面)比 \(i\) 大,而 \(i\) 右面的数比 \(i\) 小,故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j}+b_{i}+c_{i})\)
      • 连接到某个连续段的后面(如果可以在后面的话,即 \(j>[i>t]\) )时,后面填入的数(在 \(i\) 右面)比 \(i\) 大,而 \(i\) 左面的数比 \(i\) 小,故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j}+a_{i}+d_{i})\)
    • 连接两个连续段
      • \(i\) 比旁边的两个数都要大,故 \(f_{i,j}=\min(f_{i,j},f_{i-1,j+1}+2x_{i}+a_{i}+c_{i})\)
  • \(i=s\) 时只可能放到首端,可行的转移操作只有新开一段和连接到某个连续段的前面且前面不会再有数填,故 \(f_{i,j}=\min(f_{i-1,j-1}-x_{i}+d_{i},f_{i-1,j}+x_{i}+c_{i})\) ;当 \(i=e\) 时只可能放到末端,可行的转移操作只有新开一段和连接到某个连续段的后面且后面不会再有数填,故 \(f_{i,j}=\min(f_{i-1,j-1}-x_{i}+b_{i},f_{i-1,j}+x_{i}+a_{i})\)

  • 最终有 \(f_{n,1}\) 即为所求。

    点击查看代码
    ll f[5010][5010],x[5010],a[5010],b[5010],c[5010],d[5010];
    int main()
    {
        ll n,s,e,i,j;
        cin>>n>>s>>e;
        for(i=1;i<=n;i++)
        {
            cin>>x[i];
        }
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        for(i=1;i<=n;i++)
        {
            cin>>b[i];
        }
        for(i=1;i<=n;i++)
        {
            cin>>c[i];
        }
        for(i=1;i<=n;i++)
        {
            cin>>d[i];
        }
        memset(f,0x3f,sizeof(f));
        f[0][0]=0;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=i;j++)
            {
                if(i==s)
                {
                    f[i][j]=min(f[i][j],f[i-1][j-1]-x[i]+d[i]);
                    f[i][j]=min(f[i][j],f[i-1][j]+x[i]+c[i]);
                }
                else
                {
                    if(i==e)
                    {
                        f[i][j]=min(f[i][j],f[i-1][j-1]-x[i]+b[i]);
                        f[i][j]=min(f[i][j],f[i-1][j]+x[i]+a[i]);
                    }
                    else
                    {
                        if(j>(i>s)+(i>e))
                        {
                            f[i][j]=min(f[i][j],f[i-1][j-1]-2*x[i]+b[i]+d[i]);
                        }
                        if(j>(i>s))
                        {
                            f[i][j]=min(f[i][j],f[i-1][j]+b[i]+c[i]);
                        }
                        if(j>(i>e))
                        {
                            f[i][j]=min(f[i][j],f[i-1][j]+a[i]+d[i]);
                        }
                        f[i][j]=min(f[i][j],f[i-1][j+1]+2*x[i]+a[i]+c[i]);
                    }
                }
            }
        }
        cout<<f[n][1]<<endl;
        return 0;
    }
    

luogu P11007 『STA - R7』Odtlcsu

  • 观察到 \(|x|,y\) 和序列长度同阶。

  • 有解当且仅当 \(x,y\) 奇偶性相同。

    • 对一个数 \(a\) 进行和拆分(拆分成两个数相加的形式)后求平方和与求 \(a\) 的平方奇偶性相同,进一步归纳成多个数相加的形式即可。
  • 构造 \(\frac{x+y}{2}\)\(1\)\(\frac{y-x}{2}\)\(-1\) 即可。

    点击查看代码
    int main()
    {
        int x,y,i;
        cin>>x>>y;
        cout<<y<<endl;
        for(i=1;i<=(x+y)/2;i++)
        {
            cout<<1<<" ";
        }
        for(i=1;i<=(y-x)/2;i++)
        {
            cout<<-1<<" ";
        }
        return 0;
    }
    

9.6

闲话

  • 早上团体操时班主任让跳得不好的忘后站,尽量不让上面指导的老师看见我们跳得跟屎一样的操。
  • 上午颁奖时昨天的会操翻车了;数奥的开始进行全天集训;军训完 \(1\) 部的去参观西扩的学生发展中心,我们则回宿舍进行内务整理,实际上是在宿舍
  • 中午的时候生奥的就去实验集训了。
  • 下午开网络安全教育大会,夹杂着对未成年违法犯罪预防的一些内容。然后军训,会操时开始放口号的音乐,熟悉的感觉又回来了。
  • 晚上被告知要去科教馆 \(5\)\(3\) 机房做心理测试,让 \(18:50\) 在科教馆楼前门口集合,集合完毕才让进楼。因有人提前上去且生奥和数奥到位慢了会儿,班主任恼了。做完心理测试班主任让信奥的直接去机房,并嘱托 \(miaomiao\) 对我们严加管理,其他奥赛先去教室。
  • 年级部所说的学习德育图本和德育图本的测试自然被改成上奥赛了。

做题纪要

牛客 NC276055 ACM中的A题

  • 顺序结构。

    点击查看代码
    bool check(ll a,ll b,ll c)
    {
        ll tmp[4]={0,a,b,c};
        sort(tmp+1,tmp+4);
        return tmp[1]+tmp[2]>tmp[3];
    }
    int main()
    {
        ll a,b,c;
        cin>>a>>b>>c;
        if(check(a*2,b,c)||check(a,b*2,c)||check(a,b,c*2))
        {
            cout<<"Yes"<<endl;
        }
        else
        {
            cout<<"No"<<endl;
        }
        return 0;
    }
    

牛客 NC276073 ACM中的C题

  • \(n=1\) 时无解;否则两两分组进行交换,若有剩余的则再进行一次交换,次数为 \(\left\lceil \frac{n}{2} \right\rceil\)

    点击查看代码
    int main()
    {
        int n;
        cin>>n;
        if(n==1)
        {
            cout<<"-1"<<endl;
        }
        else
        {
            cout<<(int)ceil(n/2.0)<<endl;
        }
        return 0;
    }
    

牛客 NC276079 ACM中的M题

  • 同一类别单独计算即可。

    点击查看代码
    int a[200010],b[200010],cnt[200010];
    int ask(int n)
    {
        if(n==1)
        {
            cout<<"-1"<<endl;
            exit(0);
        }
        else
        {
            return (int)ceil(n/2.0);
        }
    }
    int main()
    {
        int n,ans=0,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        for(i=1;i<=n;i++)
        {
            cin>>b[i];
            a[i]=b[i];
        }
        sort(a+1,a+1+n);
        a[0]=unique(a+1,a+1+n)-(a+1);
        for(i=1;i<=n;i++)
        {
            cnt[lower_bound(a+1,a+1+a[0],b[i])-a]++;
        }
        for(i=1;i<=a[0];i++)
        {
            ans+=ask(cnt[i]);
        }
        cout<<ans<<endl;
        return 0;
    }
    

luogu P6638 「JYLOI Round 1」常规

  • 进行差分,区间查询转化成前缀和相减。

  • 先将 \(\{ a \}\) 升序排序。

  • 设当前询问的区间为 \([1,r]\) ,在 \(\{ a \}\) 中找到一个最大的 \(pos\) 使得 \(a_{pos} \le r\) ,则 \([1,r]\) 中所做常规的次数为 \(\sum\limits_{i=1}^{pos}\left\lfloor \dfrac{r-a_{i}}{k} \right\rfloor\)

  • 考虑拆开向下取整,有 \(\begin{aligned} &\sum\limits_{i=1}^{pos}\left\lfloor \dfrac{r-a_{i}}{k} \right\rfloor \\ &=\sum\limits_{i=1}^{pos} \dfrac{r-a_{i}-(r-a_{i}) \bmod k}{k} \\ &=\dfrac{1}{k} \times \sum\limits_{i=1}^{pos}r-a_{i}-(r-a_{i}) \bmod k \\ &=\dfrac{1}{k} \times (r \times pos-\sum\limits_{i=1}^{pos}a_{i}-\sum\limits_{i=1}^{pos}(r-a_{i}) \bmod k) \end{aligned}\)

  • 难点在于怎么求 \(\sum\limits_{i=1}^{pos}(r-a_{i}) \bmod k\) 。考虑按照取模运算的性质拆开并适当补上 \(+k\) ,有 \(\begin{aligned} (r-a_{i}) \bmod k=r \bmod k-a_{i} \bmod k+[r \bmod k<a_{i} \bmod k] \times k \end{aligned}\)

  • \(a_{i} \bmod k\) 为权值建立一棵主席树然后主席树上查询 \([1,pos]\) 内满足 \(a_{i} \bmod k \in (r \bmod k,k)\) 即可。

    点击查看代码
    ll a[100010],sum1[100010],sum2[100010];
    struct PDS_SMT
    {
        ll root[100010],rt_sum=0;
        struct SegmentTree
        {
            ll ls,rs,sum;
        }tree[100010<<5];
        #define lson(rt) tree[rt].ls
        #define rson(rt) tree[rt].rs
        ll build_rt()
        {
            rt_sum++;
            return rt_sum;
        }
        void update(ll pre,ll &rt,ll l,ll r,ll pos)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            tree[rt].sum++;
            if(l==r)
            {
                return;
            }
            ll mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(pre),lson(rt),l,mid,pos);
            }
            else
            {
                update(rson(pre),rson(rt),mid+1,r,pos);
            }
        }
        ll query(ll rt,ll l,ll r,ll x,ll y)
        {
            if(x<=l&&r<=y)
            {
                return tree[rt].sum;
            }
            ll mid=(l+r)/2,ans=0;
            if(x<=mid)
            {
                ans+=query(lson(rt),l,mid,x,y);
            }
            if(y>mid)
            {
                ans+=query(rson(rt),mid+1,r,x,y);
            }
            return ans;
        }
    }T;
    ll ask(ll r,ll n,ll k)
    {
        ll ans=0,pos=upper_bound(a+1,a+1+n,r)-a-1;
        ans+=r*pos;
        ans-=sum1[pos];
        ans-=(r%k)*pos;
        ans+=sum2[pos];
        ans-=T.query(T.root[pos],0,k-1,r%k+1,k-1)*k;
        return ans/k;
    }
    int main()
    {
        ll type,n,m,k,mod,l,r,ans=0,i;
        cin>>type>>n>>m>>k;
        if(type==1)
        {
            cin>>mod;
        }
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        for(i=1;i<=n;i++)
        {
            sum1[i]=sum1[i-1]+a[i];
            sum2[i]=sum2[i-1]+a[i]%k;
            T.update(T.root[i-1],T.root[i],0,k-1,a[i]%k);
        }
        cin>>l>>r;
        ans=ask(r,n,k)-ask(l-1,n,k);
        cout<<ans<<endl;
        for(i=2;i<=m;i++)
        {
            cin>>l>>r;
            if(type==1)
            {
                l=(l+ans-1)%mod+1;
                r=(r+ans-1)%mod+1;
                if(l>r)
                {
                    swap(l,r);
                }
            }
            ans=ask(r,n,k)-ask(l-1,n,k);
            cout<<ans<<endl;
        }
        return 0;
    }
    

9.7

闲话

  • 早上武术团体操,还好。
  • 上午让去参观西扩学生发展中心,因参观速度过快,班主任又跟德育主任说让我们去参观动物(实际只参观了一半),然后接着回去军训。
  • 下午军训完是心理素质提升活动。
  • 晚上改奥赛,班里没有人自然年级部就没法抽查德育图本的学习情况。

做题纪要

牛客 NC276120 ACM中的CM题

  • 一边排雷一边提升排雷能力显然不如一开始提升排雷能力然后进行排雷优。

  • 答案上界显然为值域大小。

  • 观察到值域较小,考虑枚举答案然后进行 \(check\)

  • 直接进行 \(check\) 不太好做考虑枚举提升排雷能力的次数然后算出排雷的次数最后取 \(\min\) 即可。

    点击查看代码
    int a[200010];
    int check(int x,int n)
    {
        int ans=x;
        for(int i=1;i<=n;i=upper_bound(a+1,a+1+n,a[i]+x)-a)
        {
            ans++;
        }
        return ans;
    }
    int main()
    {
        int n,ans=0x7f7f7f7f,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        for(i=0;i<=200000;i++)
        {
            ans=min(ans,check(i,n));
        }
        cout<<ans<<endl;
        return 0;
    }
    

[ABC370A] Raise Both Hands

  • 选择结构。

    点击查看代码
    int main()
    {
        int l,r;
        cin>>l>>r;
        if((l==1&&r==1)||(l==0&&r==0))
        {
            cout<<"Invalid"<<endl;
        }
        else
        {
            if(l==1&&r==0)
            {
                cout<<"Yes"<<endl;
            }
            else
            {
                cout<<"No"<<endl;
            }
        }
        return 0;
    }
    

[ABC370B] Binary Alchemy

  • 模拟。

    点击查看代码
    int a[110][110];
    int main()
    {
        int n,ans,i,j;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=i;j++)
            {
                cin>>a[i][j];
            }
        }
        ans=a[1][1];
        for(i=2;i<=n;i++)
        {
            ans=a[max(ans,i)][min(ans,i)];
        }
        cout<<ans<<endl;
        return 0;
    }
    

[ABC370C] Word Ladder

  • 百度翻译害死人。

  • 贪心,优先改字典序靠前的。

    点击查看代码
    string s,t;
    int main()
    {
        int cnt=0,i;
        cin>>s>>t;
        for(i=0;i<s.size();i++)
        {
            cnt+=(s[i]!=t[i]);
        }
        cout<<cnt<<endl;
        for(i=0;i<s.size();i++)
        {
            if(s[i]>t[i])
            {
                s[i]=t[i];
                cout<<s<<endl;
            }
        }
        for(i=s.size()-1;i>=0;i--)
        {
            if(s[i]<t[i])
            {
                s[i]=t[i];
                cout<<s<<endl;
            }
        }
        return 0;
    }
    

9.8

闲话

  • 早上武术团体操。
  • 上午军训。
  • 下午先是宿舍内的消防演练活动,教室的说是 \(14:45\) 拉响警报铃但实际在 \(14:18\) 就拉响了,可能有点符合出其不意的目的(?);然后军训;接着是后天闭营仪式的彩排,并得知了若闭营仪式不理想需要额外多军训 \(5\) 天。
  • 因下午彩排不太理想,晚上加练。其他奥赛尝试找各自教练交涉能不能旷掉但没找到自己教练,到操场的时候还被班主任骂了一顿。因班主任都到操场来监督了所以之后来加练。临结束的时候班主任说“我说两句哈。算了吧,回班再说”,遂晚上的奥赛课没了,只好回班上自习。到班后班主任说了下班里今年化学、数学、物理的期望优秀获奖情况,然后选了下课代表,并喂了点鸡汤。 @HANGRY_sol@Pursuing_OIer 直接去机房了,但 @HANGRY_sol 没有被察觉,反倒是 @Pursuing_OIer 被“强制征兵”做了生物课代表。

做题纪要

9.9

闲话

  • 早上团体操。
  • 上午军训完是闭营仪式彩排。
  • 下午起床后让在宿舍收拾内务遂多睡了会儿;军训完是闭营仪式彩排。
    • 抽象内容
      • 休息 \(20 \min\) 到一半忽然被主席台上的教官叫起来说让玩个游戏,但实际上是坐在原地并在没有音乐伴奏的情况下唱校歌且尽可能让众教官听懂我们在唱什么。
      • “累不累”

        (刚跑完一圈并变换队形后,操场上的众人还在交流)

        主席台上的教官:你们累不累

        操场上的众人:累

        主席台上的教官:你们要是累的话在队列里面就没有精力再去交流,看来还是不累,再跑一圈

        (又跑完一圈并变换队形后,操场上的众人没有交流)
        主席台上的教官:你们累不累

        操场上的众人:累

        主席台上的教官:你们要是累的话在队列里面就没有精力再去回答我,看来还是不累,再跑一圈

        (于是又跑了一圈并变换队形)

  • 晚上听班长讲班主任说是学科自习,但貌似有教练的交涉,班主任让在班里的 @HANGRY_sol@Charlie_ljk 的去机房了(剩下的人员以“不知道晚上是学科自习”为由直接去的机房),还说让他们什么时候上课/不上课都提前说好。
  • 到机房后 \(field\) 听我们在谈论学校内网上还没有学生照片显示加翻阅各班看有没有认识的人,然后不知道怎么就想到了问我们是不是还没拍照片,想不想挂初中时候的照片,然后说要给 @HANGRY_sol 挂上初中照片于是就改了,后面尝试挂 luogu 或博客园头像但没有付诸实践,毕竟可能被扣上“滥用职权”的帽子。

做题纪要

[ABC370D] Cross Explosion

  • set 里插入哨兵方便查询。

    点击查看代码
    vector<int>vis[400010];
    set<int>hang[400010],lie[400010];
    set<int>::iterator it;
    int main()
    {
        int n,m,q,x,y,ans=0,i,j;
        cin>>n>>m>>q;
        for(i=1;i<=n;i++)
        {
            vis[i].resize(m+5);
            hang[i].insert(-0x7f7f7f7f);
            hang[i].insert(0x7f7f7f7f);
            for(j=1;j<=m;j++)
            {
                vis[i][j]=1;
                hang[i].insert(j);
                lie[j].insert(i);
            }
        }
        for(j=1;j<=m;j++)
        {
            lie[j].insert(-0x7f7f7f7f);
            lie[j].insert(0x7f7f7f7f);
        }
        for(i=1;i<=q;i++)
        {
            cin>>x>>y;
            if(vis[x][y]==1)
            {
                vis[x][y]=0;
                ans++;
                hang[x].erase(hang[x].lower_bound(y));
                lie[y].erase(lie[y].lower_bound(x));
            }
            else
            {
                it=--hang[x].upper_bound(y);
                if(*it!=-0x7f7f7f7f)
                {
                    vis[x][*it]=0;
                    ans++;
                    lie[*it].erase(lie[*it].lower_bound(x));
                    hang[x].erase(it);
                }
                it=hang[x].upper_bound(y);
                if(*it!=0x7f7f7f7f)
                {
                    vis[x][*it]=0;
                    ans++;
                    lie[*it].erase(lie[*it].lower_bound(x));
                    hang[x].erase(it);
                }
                it=--lie[y].upper_bound(x);
                if(*it!=-0x7f7f7f7f)
                {
                    vis[*it][y]=0;
                    ans++;
                    hang[*it].erase(hang[*it].lower_bound(y));
                    lie[y].erase(it);
                }
                it=lie[y].upper_bound(x);
                if(*it!=0x7f7f7f7f)
                {
                    vis[*it][y]=0;
                    ans++;
                    hang[*it].erase(hang[*it].lower_bound(y));
                    lie[y].erase(it);
                }
            }
        }
        cout<<n*m-ans<<endl;
        return 0;
    }
    

[ABC370E] Avoid K Partition

  • \(sum_{i}=\sum\limits_{j=1}^{i}a_{j}\)

  • \(f_{i}\) 表示处理到 \(i\) 时合法的方案数,状态转移方程为 \(f_{i}=\sum\limits_{j=1}^{i}[sum_{i}-sum_{j-1} \ne k] \times f_{j-1}=\sum\limits_{j=0}^{i-1}[sum_{i}-sum_{j} \ne k] \times f_{j}\) ,边界为 \(f_{0}=1\)

  • 开一个桶存 \(sum_{j}\) 然后相减即可。

    点击查看代码
    ll a[200010],sum[200010],f[200010];
    map<ll,ll>g;
    int main()
    {
        ll n,k,num,i;
        cin>>n>>k;
        g[0]=num=f[0]=1;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            sum[i]=sum[i-1]+a[i];
            f[i]=(num-g[sum[i]-k]+p)%p;
            g[sum[i]]=(g[sum[i]]+f[i])%p;
            num=(num+f[i])%p;
        }
        cout<<f[n]<<endl;
        return 0;
    }
    

牛客NC276152 逆序数

  • 两个数正序若不形成逆序对则逆序后一定形成逆序对。故 \(\sum\limits_{i=1}^{i-1}i-k\) 即为所求。

    点击查看代码
    int main()
    {
        ll n,k;
        cin>>n>>k;
        cout<<n*(n-1)/2-k<<endl;
        return 0;
    }
    

9.10

闲话

  • \(3,4\) 点时听见外面开始下雨了。
  • 早上起床后发现雨还在下,所以吃完早饭直接去教室了。到教室后发现没有几个人按照原闭营仪式 \(6:20\) 的时间到位,一问发现到了的几个等着上完早读再去吃早饭。
    • 从宿舍到食堂的一小段路水已经能没过脚脖子了,只好垫着脚尖过。
    • 宿舍里没放伞遂只好淋雨了。
  • 下午 \(16:00\) 开始闭营仪式,但是让下了第七节课的大课间就下去彩排。正式开始 \(10 \min\) 左右又开始下雨,但一部某年级主任硬是让闭营仪式接着进行,于是就有了淋着雨听领导讲话、淋着雨看海航班和刺杀班演示、淋着雨跑操、淋着雨跳团体操、淋着雨唱 HZ 校歌等活动。结束后让我们有序回班上自习,但因为下节课是奥赛自习遂直接去机房了。
  • 临近吃晚饭时突然发现机房众人都不知道几点去吃晚饭。 \(18:05\) 出机房后到楼底下发现没人,我们就分成了两部分人。一部分人直接去食堂吃饭,另一部分人回教学楼在某个地方躲起来等着打下课铃。到食堂后发现刚到 \(18:10\) ,到 \(18:15\) 才陆陆续续有高二的下来吃饭,我们遂做好了要写检讨的准备。等到 \(18:23\) 左右出食堂时才发现一部的才出来吃饭,走到班外的时候发现他们 \(18:28\) 刚下课,班主任还在上面盯着,他们前脚刚走我们后脚就进去了。然后发现貌似班主任不知道我们提前 \(23 \min\) 下课的事情,逃过一劫。
  • 晚上看教师节晚会。

做题纪要

[ABC370F] Cake Division

  • 破环为链和二分答案是显然的。

  • 枚举断点进行 \(check\) 的时间复杂度为 \(O(n^{2} \log n)\) ,不可接受。

  • 分割成 \(k\) 段的本质是在满足区间和 \(\ge mid\) 的情况下往后跳 \(k\) 次。设 \(f_{i,j}\) 表示从第 \(i\) 个位置开始分配 \(j\) 个人后第 \(j+1\) 个人的初位置,双指针维护即可。

  • 枚举断点的 \(O(n)\) 是不可避免的,考虑倍增优化跳的过程。

  • 此时若 \(f_{i,k} \le i+n\) 说明可以作为断点,即可以被分配,统计数量后用 \(n\) 相减即可,时间复杂度为 \(O(n \log n \log V)\)

    点击查看代码
    ll a[400010],sum[400010],f[400010][25],N;
    ll check(ll mid,ll n,ll k)
    {
        for(ll l=1,r=1;l<=2*n;l++)
        {
            while(r<=2*n-1&&(sum[r]-sum[l-1]<mid||r<l))
            {
                r++;
            }
            f[l][0]=r+1;
        }
        f[2*n+1][0]=2*n+1;
        for(ll j=1;j<=N;j++)
        {
            for(ll i=1;i<=2*n+1;i++)
            {
                f[i][j]=f[f[i][j-1]][j-1];
            }
        }
        ll ans=0,pos;
        for(ll i=1;i<=n;i++)
        {
            pos=i;
            for(ll j=N;j>=0;j--)
            {
                if((k>>j)&1)
                {
                    pos=f[pos][j];
                }
            }
            ans+=(pos<=i+n);
        }
        return ans;
    }
    int main()
    {
        ll n,k,l=0,r=0x7f7f7f7f,mid,tmp,ans1=0,ans2=0,i;
        cin>>n>>k;
        N=log2(2*n)+1;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            a[i+n]=a[i];
        }
        for(i=1;i<=2*n;i++)
        {
            sum[i]=sum[i-1]+a[i];
        }
        while(l<=r)
        {
            mid=(l+r)/2;
            tmp=check(mid,n,k);
            if(tmp>=1)
            {
                ans1=mid;
                ans2=tmp;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        cout<<ans1<<" "<<n-ans2<<endl;
        return 0;
    }
    
  • 或者以 \(f\) 为边建树,跳的过程就转化为了求 \(k\) 级祖先/祖孙,因为 \(k\) 是改定的所以可以记录 \(DFS\) 时经过的点求 \(k\) 级祖先,建议参考 关于树上求 K-son 和 K-father 的各种方法 ,时间复杂度为 \(O(n \log V)\)

    • 如果你 \(RE\) 了可能是因为双指针的写法有问题导致建了自环。
    点击查看代码
    struct node
    {
        ll nxt,to;
    }e[400010];
    ll head[400010],a[400010],sum[400010],s[400010],fa[400010],top,cnt;
    void add(ll u,ll v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs(ll x,ll k)
    {
        top++;
        s[top]=x;
        fa[x]=(top>=k?s[top-k]:0);
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            dfs(e[i].to,k);
        }
        top--;
    }
    ll check(ll mid,ll n,ll k)
    {
        cnt=top=0;
        memset(e,0,sizeof(e));
        memset(head,0,sizeof(head));
        memset(fa,-1,sizeof(fa));
        for(ll l=1,r=1;l<=2*n;l++)
        {
            while(r<=2*n-1&&(sum[r]-sum[l-1]<mid||r<l))
            {
                r++;
            }
            add(r+1,l);
        }
        for(ll i=2*n+1;i>=1;i--)
        {
            if(fa[i]==-1)
            {
                dfs(i,k);
            }
        }
        ll ans=0;
        for(ll i=1;i<=n;i++)
        {
            ans+=(fa[i]!=0&&fa[i]<=i+n);
        }
        return ans;
    }
    int main()
    {
        ll n,k,l=0,r=2e9,mid,tmp,ans1=0,ans2=0,i;
        cin>>n>>k;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            a[i+n]=a[i];
        }
        for(i=1;i<=2*n;i++)
        {
            sum[i]=sum[i-1]+a[i];
        }
        while(l<=r)
        {
            mid=(l+r)/2;
            tmp=check(mid,n,k);
            if(tmp>=1)
            {
                ans1=mid;
                ans2=tmp;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        cout<<ans1<<" "<<n-ans2<<endl;
        return 0;
    }
    
posted @ 2024-09-02 20:07  hzoi_Shadow  阅读(98)  评论(8编辑  收藏  举报
扩大
缩小