LOADING

加载过慢请开启缓存 浏览器默认开启

叫兽の窝

The 2022 ICPC Asia Nanjing Regional Contest

题解 2024/9/17

D. Chat Program

题目

You’re the researcher of the International Chat Program Company (ICPC). Today, you discover the following chat history when reviewing some research data.

SUA (2022/12/04 23:01:25)

I’m out of ideas for competitive programming problems! Please give me a problem about sequences.

BOT (2022/12/04 23:01:27)

Sure. Here is a competitive programming problem about sequences.

Given an integer sequence $a_1, a_2, \cdots, a_n$ of length $n$ and four other integers $k$, $m$, $c$ and $d$, your goal is to maximize the $k$-th largest element in the sequence.

To achieve the goal, you can perform the following operation at most once: select a continuous sub-array of length $m$ and add an arithmetic sequence with length $m$, initial term $c$ and common difference $d$ to the sub-array.

More formally, you can select an integer $p$ satisfying $1 \le p \le n - m + 1$ and add $(c + di)$ to $a_{p + i}$ for all $0 \le i < m$.

Calculate the largest possible value of the $k$-th largest element in the sequence after at most one operation.

The $k$-th largest element in the sequence is the $k$-th element in the sorted sequence after sorting all elements from the largest to the smallest. For example, the $3$rd largest element in sequence ${5, 7, 1, 9}$ is $5$, while the $3$rd largest element in sequence ${9, 7, 5, 9}$ is $7$.

SUA (2022/12/05 00:15:17)

This problem seems difficult! Please teach me the solution.

BOT (2022/12/05 00:15:30)

Sure. Firstly, we can…

[DATA EXPUNGED]

Unfortunately, parts of the chat history are lost due to a disk failure. You’re amazed at how a chat program can create a competitive programming problem. To verify whether the chat program can create valid problems, you decide to try on this problem.

image-20240917151751463

思路

二分答案,如果一个数想成为数组中的第k大,那么就需要至少k-1个比他大的数存在,在check函数中,我们把比mid大的数字状态更新为1,不做考虑,所有状态为0的点在need数组中记录达到mid所需要的最小项数,现在要做的就是维护所有状态为0的点,让他们尽可能多的变成比mid大的数字,当这些数字满足>=k时,则当前的mid是满足条件的

因为等差数列是连续加在数组上的,那么不妨把这个等差数列想象成一个滑动窗口m,m的第i个数代表等差数列的第i项,在移动窗口的时候窗口尾部每扫描到一个为0的点,判断一下他的need是否小于m的尾项,如果是,就加入到一个优先队列里面(优先队列用来存储当前元素弹出的下标idx,根据idx的大小关系排序,越小的弹出时间越早),否则直接略过,在每一次挪动窗口加入值之后用while循环判断当前有多少元素已经不满足条件。遍历一遍维护优先队列的最大size即可,加上之前已经大于mid的元素个数与k进行比较。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
int n,k,m,c,d,a[N];
bool st[N];
int need[N];
bool check(int mid){
    for(int i=1;i<=n;i++){  
        if(a[i]>=mid) st[i]=1;
        else st[i]=0;
        need[i]=0;
    }
    for(int i=1;i<=n;i++){
        if(!st[i]){
            if(mid-a[i]<=c) need[i]=1;
            else{
                if(d==0) need[i]=1e9; //公差为0时,首项不满足,之后任何一项都不会满足,所以need[i]记录无限大
                else
                need[i]=(mid-a[i]-c+(d-1))/d+1; 
            }
        }
    }
    priority_queue<int,vector<int>,greater<int>>q;
    for(int i=1;i<=m;i++){
        if(st[i]==0&&need[i]<=i) q.push(m+(i-need[i])+1);
    }
    int ans=q.size();
    for(int i=m+1;i<=n;i++){
        if(st[i]==0&&need[i]<=m) q.push(i+(m-need[i])+1);
        while(!q.empty()&&q.top()<=i){
            q.pop();
        }
        ans=max(ans,(int)q.size());
    }
    for(int i=1;i<=n;i++){
        if(st[i]) ans++;
    }
    if(ans>=k) return 0;
    else return 1;
}
void solve()
{
    cin>>n>>k>>m>>c>>d;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int l=0,r=1e17;
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) r=mid-1;
        else l=mid;
    }
    cout<<l<<endl;
}
signed main()
{
    int t=1;
    while(t--)
    {
        solve();
    }
}
阅读全文

Codeforces Round 972 (Div. 2)

题解 2024/9/16

C. Lazy Narek

题目

Narek is too lazy to create the third problem of this contest. His friend Artur suggests that he should use ChatGPT. ChatGPT creates $n$ problems, each consisting of $m$ letters, so Narek has $n$ strings. To make the problem harder, he combines the problems by selecting some of the $n$ strings possibly none and concatenating them without altering their order. His chance of solving the problem is defined as $score_n - score_c$, where $score_n$ is Narek’s score and $score_c$ is ChatGPT’s score.

Narek calculates $score_n$ by examining the selected string (he moves from left to right). He initially searches for the letter $\texttt{“n”}$, followed by $\texttt{“a”}$, $\texttt{“r”}$, $\texttt{“e”}$, and $\texttt{“k”}$. Upon finding all occurrences of these letters, he increments $score_n$ by $5$ and resumes searching for $\texttt{“n”}$ again (he doesn’t go back, and he just continues from where he left off).

After Narek finishes, ChatGPT scans through the array and increments $score_c$ by $1$ for each letter $\texttt{“n”}$, $\texttt{“a”}$, $\texttt{“r”}$, $\texttt{“e”}$, or $\texttt{“k”}$ that Narek fails to utilize (note that if Narek fails to complete the last occurrence by finding all of the $5$ letters, then all of the letters he used are counted in ChatGPT’s score $score_c$, and Narek doesn’t get any points if he doesn’t finish finding all the 5 letters).

Narek aims to maximize the value of $score_n - score_c$ by selecting the most optimal subset of the initial strings.

实现

定义dp[i] [j]为前i个字符串,最后一个字符串匹配到了j个字母(n–1,a–2,r–3,e–4,k–5),每一次更新当前字符串分别以前某个字符串需要的字母为开头开始计数,算出以这个字母开头的分数差,进行状态转移,最后在dp[n] [i]中枚举一下最优解减去i+1即为答案

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e3+10;
int a[N],b[N],ans;
int dp[N][6];
int sum[6],need[6];
map<char,bool>st;

string temp="?narek";
int n,m;
void init(string s){
    for(int i=1;i<=5;i++) sum[i]=0,need[i]=i;
    for(int i=1;i<=m;i++){
        if(st[s[i]]){
            for(int j=1;j<=5;j++){
                if(s[i]==temp[need[j]]){
                    need[j]++;
                    if(need[j]==6) sum[j]+=5,need[j]=1;
                }
                else sum[j]--;
            }
        }
    }
}
void solve()
{
    cin>>n>>m;
    for(int i=0;i<=n;i++){
        for(int j=1;j<=6;j++){
            dp[i][j]=-1e4;
        }
    }
    dp[0][1]=0;
    for(int i=1;i<=n;i++){
        string s;
        cin>>s;
        s=' '+s;
        init(s);
        for(int j=1;j<=5;j++) dp[i][j]=dp[i-1][j];
        for(int j=1;j<=5;j++) dp[i][need[j]]=max(dp[i][need[j]],dp[i-1][j]+sum[j]);
    }
    int ans=0;
    for(int i=1;i<=5;i++){
        ans=max(ans,dp[n][i]-i+1);
    }
    cout<<ans<<endl;
}
signed main()
{
    st['n']=1,st['a']=1,st['r']=1,st['e']=1,st['k']=1;
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}
阅读全文

记录最优方案数的dp

算法 2024/9/13

低价购买

题目描述

“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价,你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

这里是某支股票的价格清单:

$$
\def\arraystretch{1.5}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|}\hline
\textsf{日期} & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 \cr\hline
\textsf{价格} & 68 & 69 & 54 & 64 & 68 & 64 & 70 & 67 & 78 & 62& 98 & 87 \cr\hline
\end{array}
$$
最优秀的投资者可以购买最多 $4$ 次股票,可行方案中的一种是:
$$
\def\arraystretch{1.5}
\begin{array}{|c|c|c|c|c|}\hline
\textsf{日期} & 2 & 5 & 6 & 10 \cr\hline
\textsf{价格} & 69 & 68 & 64 & 62 \cr\hline
\end{array}
$$

输入格式

第一行共一个整数 $N\ (1 \le N \le 5000)$,股票发行天数

第二行一行 $N$ 个整数,是每天的股票价格。保证是大小不超过 $2^{16}$ 的正整数。

输出格式

输出共一行两个整数,分别为最大购买次数和拥有最大购买次数的方案数(数据保证 $ \le 2^{31}$)当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这 $2$ 种方案被认为是相同的。

样例输入 #1

12
68 69 54 64 68 64 70 67 78 62 98 87

样例输出 #1

4 2

思路

f[i]表示以数字a[i]结尾且长度为dp[i]的方案数,那么当a[i]!=a[j]时,出现dp[i]=dp[j]+1的情况,直接在**f[i]加上f[j],但当a[j]=a[i]时,因为我们对f[i]的定义可得,f[i]f[j]**此时是有重合的,也就是说只保留一个最多方案的f[i]即可

最后遍历一遍数组,加上所有等于答案长度的方案数即可

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=1e5+10;
const int mod=1e9+7;
int dp[N],a[N];
int f[N];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
          cin>>a[i];
          dp[i]=f[i]=1;
    }
    int ans1=1;
    for(int i = 2; i <= n; i++) {
          for(int j = 1; j < i; j++){
               if(a[i] < a[j]) {
                    if(dp[i]<dp[j]+1){
                         dp[i] = max(dp[i], dp[j] + 1);
                         f[i]=f[j];
                    }
                    else if(dp[i]==dp[j]+1) f[i]+=f[j];
               }
               if(a[i]==a[j]) dp[j]=f[j]=0;
          }
          ans1 = max(ans1, dp[i]);
     }
     int num=0;
     for(int i=1;i<=n;i++){
          if(dp[i]==ans1) num+=f[i];
     }
     cout<<ans1<<' '<<num<<endl;
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}

同样的记录背包最优方案数在acwing有一道板题

image-20240913105957884

这里用g[i]表示背包体积为i时的最优方案数

如果选第i个物品能做到让价值增加,那么舍弃掉原先的方案数,用g[j-v]的值来覆盖掉g[j];

如果选与不选对结果没有影响,即价值一样时,需要把两种状态的方案汇总到一起,g[j]+=g[j-v];

最后的g[V]即是最优情况下的方案总和

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7;
int dp[N];
int a[N],b[N];
int n,V;
vector<int>e[N];
int g[N];

void solve()
{
   int n,V;
   cin>>n>>V;
   for(int i=0;i<=V;i++) g[i]=1;
   for(int i=1;i<=n;i++)
   {
        int v,w;
        cin>>v>>w;
        for(int j=V;j>=v;j--)
        {
            if(dp[j]<dp[j-v]+w)
            {
                dp[j]=dp[j-v]+w;
                g[j]=g[j-v];
            }
            else if(dp[j]==dp[j-v]+w)
            {
                g[j]+=g[j-v];
                g[j]%=mod;
            }
        }
   }
   cout<<g[V]<<endl;
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}
阅读全文

状压dp

算法 2024/9/11

AcWing 1064. 小国王【线性状压DP+目标状态优化】

image-20240911025041124

img

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int mod=1e9+7;
int n,k;
int cnt[N];
int dp[12][110][3000];
vector<int>head[N];
vector<int>state;
bool check(int st){
    for(int i=0;i<n;i++)
        if((st>>i&1)&&st>>(i+1)&1) return 0;
    return 1;
}
int count(int st){
    int res=0;
    for(int i=0;i<n;i++){
        res+=st>>i&1;
    }
    return res;
}
void solve(){
    cin>>n>>k;
    for(int i=0;i<1<<n;i++){
        if(check(i)) state.push_back(i);
        cnt[i]=count(i);
    }
    for(int i=0;i<state.size();i++){
        for(int j=0;j<state.size();j++){
            int x=state[i],y=state[j];
            if(((x&y)==0)&&check(x|y)) head[i].push_back(j);
        }
    }
    dp[0][0][0]=1;
    for(int i=1;i<=n+1;i++){
        for(int j=0;j<=k;j++){
            for(int a=0;a<state.size();a++){
                for(int b:head[a]){
                    int c=cnt[state[a]];
                    if(j>=c){
                        dp[i][j][a]+=dp[i-1][j-c][b];
                    }
                }
            }
        }
    }
    cout<<dp[n+1][k][0]<<endl;
}
signed main(){
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}

AcWing 327. 玉米田【线性状压DP+目标状态优化】

image-20240911025428056

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int mod=1e8;
int n,k;
int cnt[N],a[110];
int dp[14][1<<14];
vector<int>head[1<<14];
vector<int>state;
bool check(int st){
    return !(st&st<<1);
}
int count(int st){
    int res=0;
    for(int i=0;i<n;i++){
        res+=st>>i&1;
    }
    return res;
}
void solve(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>k,a[i]|=!k<<(j-1);
        }
    }
    for(int st=0;st<1<<m;st++){
        if(check(st)) state.push_back(st);
    }
    for(auto it:state){
        for(auto next_it:state){
            if(!(it&next_it)) head[it].push_back(next_it);
        }
    }
    dp[0][0]=1;
    for(int i=1;i<=n+1;i++){
        for(auto st:state){
            if(!(st&a[i])){
                for(auto next_st:head[st]){
                    dp[i][st]+=dp[i-1][next_st];
                    dp[i][st]%=mod;
                }
            }
        }
    }
    cout<<dp[n+1][0]<<endl;
}
signed main(){
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}

[NOI2001] 炮兵阵地

题目描述

司令部的将军们打算在 $N\times M$ 的网格地图上部署他们的炮兵部队。

一个 $N\times M$ 的地图由 $N$ 行 $M$ 列组成,地图的每一格可能是山地(用 $\texttt{H}$ 表示),也可能是平原(用 $\texttt{P}$ 表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

样例输入 #1

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

样例输出 #1

6
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int mod=1e9+7;
int n,m;
char c;
int cnt[N],g[110];
int dp[2][1<<10][1<<10];
vector<int>head[N];
vector<int>state;
bool check(int st){
    return !(st&st>>1||st&st>>2);
}
int count(int st){
    int res=0;
    for(int i=0;i<m;i++){
        res+=st>>i&1;
    }
    return res;
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>c, g[i]|=(c=='H')<<(j-1);
        }
    }
    for(int st=0;st<1<<m;st++){
        if(check(st)) state.push_back(st),cnt[st]=count(st);
    }
    for(int x: state){
        for(int y: state){
            if(!(x&y)) head[x].push_back(y);
        }
    }
    for(int i=1;i<=n+2;i++){
        for(int st:state){
            if(!(st&g[i])){
                for(int p1:head[st]){
                    for(int p2:head[p1]){
                        if(!(st&p2)){
                            dp[i&1][st][p1]=max(dp[i&1][st][p1],dp[i-1&1][p1][p2]+cnt[st]);
                        }
                    }
                }
            }
        }
    }
    cout<<dp[n+2&1][0][0]<<endl;
}
signed main(){
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}
阅读全文

AcWing 734. 能量石

算法 2024/9/10

岩石怪物杜达生活在魔法森林中,他在午餐时收集了 N 块能量石准备开吃。

由于他的嘴很小,所以一次只能吃一块能量石。

能量石很硬,吃完需要花不少时间。

吃完第 ii块能量石需要花费的时间为 Si秒。

杜达靠吃能量石来获取能量。

不同的能量石包含的能量可能不同。

此外,能量石会随着时间流逝逐渐失去能量。

第 ii块能量石最初包含 Ei 单位的能量,并且每秒将失去 Li 单位的能量。

当杜达开始吃一块能量石时,他就会立即获得该能量石所含的全部能量(无论实际吃完该石头需要多少时间)。

能量石中包含的能量最多降低至 0。

请问杜达通过吃能量石可以获得的最大能量是多少?

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含整数 N,表示能量石的数量。

接下来 N 行,每行包含三个整数 Si,Ei,Li

输出格式

每组数据输出一个结果,每个结果占一行。

结果表示为 Case #x: y,其中 xx 是组别编号(从 1 开始),y 是可以获得的最大能量值。

数据范围

1≤T≤10
1≤N≤100
1≤Si≤100
1≤Ei≤105
0≤Li≤105

输入样例:

3
4
20 10 1
5 30 5
100 30 1
5 80 60
3
10 4 1000
10 3 1000
10 8 1000
2
12 300 50
5 200 0

输出样例:

Case #1: 105
Case #2: 8
Case #3: 500

样例解释

在样例#1中,有 N=4 个宝石。杜达可以选择的一个吃石头顺序是:

  • 吃第四块石头。这需要 5 秒,并给他 80 单位的能量。
  • 吃第二块石头。这需要 5 秒,并给他 5 单位的能量(第二块石头开始时具有 30 单位能量,5 秒后失去了 25 单位的能量)。
  • 吃第三块石头。这需要 100 秒,并给他 20 单位的能量(第三块石头开始时具有 30 单位能量,10 秒后失去了 10 单位的能量)。
  • 吃第一块石头。这需要 20 秒,并给他 0 单位的能量(第一块石头以 10 单位能量开始,110 秒后已经失去了所有的能量)。

他一共获得了 105105 单位的能量,这是能获得的最大值,所以答案是 105。

在样本案例#2中,有 N=3 个宝石。

无论杜达选择吃哪块石头,剩下的两个石头的能量都会耗光。

所以他应该吃第三块石头,给他提供 8 单位的能量。

在样本案例#3中,有 N=2 个宝石。杜达可以:

  • 吃第一块石头。这需要 12 秒,并给他 300单位的能量。
  • 吃第二块石头。这需要 5秒,并给他 200 单位的能量(第二块石头随着时间的推移不会失去任何能量!)。

所以答案是 500。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7;
int dp[N];
struct tr
{
    int s,e,l;
    bool operator < (const tr &a) const{
        return s*a.l<a.s*l;
    }
}tree[N];
int t,num=0;
void solve()
{
    int n,m=0;
    cin>>n;
    memset(dp,-127,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=n;i++)
    {
        int s,e,l;
        cin>>s>>e>>l;
        tree[i]={s,e,l};
        m+=s;
    }
    sort(tree+1,tree+1+n);
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=tree[i].s;j--)
        {
            dp[j]=max(dp[j],dp[j-tree[i].s]+tree[i].e-tree[i].l*(j-tree[i].s));
        }
    }
    int ans=0;
    for(int i=0;i<=m;i++)
    {
        ans=max(ans,dp[i]);
    }
    cout<<"Case #"<<num<<": "<<ans<<endl;
}
signed main()
{
    cin>>t;
    while(num<t)
    {
        num++;
        solve();
    }
}
阅读全文

AtCoder Beginner Contest 370

题解 2024/9/10

D - Cross Explosion

Problem Statement

There is a grid with $H$ rows and $W$ columns. Let $(i, j)$ denote the cell at the $i$-th row from the top and $j$-th column from the left.
Initially, there is one wall in each cell.
After processing $Q$ queries explained below in the order they are given, find the number of remaining walls.

In the $q$-th query, you are given two integers $R_q$ and $C_q$.
You place a bomb at $(R_q, C_q)$ to destroy walls. As a result, the following process occurs.

  • If there is a wall at $(R_q, C_q)$, destroy that wall and end the process.
  • If there is no wall at $(R_q, C_q)$, destroy the first walls that appear when looking up, down, left, and right from $(R_q, C_q)$. More precisely, the following four processes occur simultaneously:
    • If there exists an $i \lt R_q$ such that a wall exists at $(i, C_q)$ and no wall exists at $(k, C_q)$ for all $i \lt k \lt R_q$, destroy the wall at $(i, C_q)$.
    • If there exists an $i \gt R_q$ such that a wall exists at $(i, C_q)$ and no wall exists at $(k, C_q)$ for all $R_q \lt k \lt i$, destroy the wall at $(i, C_q)$.
    • If there exists a $j \lt C_q$ such that a wall exists at $(R_q, j)$ and no wall exists at $(R_q, k)$ for all $j \lt k \lt C_q$, destroy the wall at $(R_q, j)$.
    • If there exists a $j \gt C_q$ such that a wall exists at $(R_q, j)$ and no wall exists at $(R_q, k)$ for all $C_q \lt k \lt j$, destroy the wall at $(R_q, j)$.
#include <bits/stdc++.h>
#define fi first
#define se second
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

signed main() {
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int n, m, q;
    cin >> n >> m >> q;

    vector<set<int>> col(m + 1), lin(n + 1);
    vector<vector<bool>> st(n + 1, vector<bool>(m + 1, 0));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) col[j].insert(i), lin[i].insert(j), st[i][j] = 1;
    int res = n * m;
    while (q--) {
        int x, y;
        cin >> x >> y;

        if (st[x][y]) {
            res--, st[x][y] = 0;
            col[y].erase(x), lin[x].erase(y);
            continue;
        }
        auto up = col[y].upper_bound(x), down = up, left = lin[x].upper_bound(y), right = left;
        if (up != col[y].begin()) {
            up--, res--, st[*up][y] = 0;
            col[y].erase(up), lin[*up].erase(y);
        }
        if (down != col[y].end()) {
            res--, st[*down][y] = 0;
            col[y].erase(down), lin[*down].erase(y);
        }
        if (left != lin[x].begin()) {
            left--, res--, st[x][*left] = 0;
            lin[x].erase(left), col[*left].erase(x);
        }
        if (right != lin[x].end()) {
            res--, st[x][*right] = 0;
            lin[x].erase(right), col[*right].erase(x);
        }
    }

    cout << res << endl;

    return 0;
}

E - Avoid K Partition

Problem Statement

You are given a sequence $A = (A_1, A_2, \dots, A_N)$ of length $N$ and an integer $K$.
There are $2^{N-1}$ ways to divide $A$ into several contiguous subsequences. How many of these divisions have no subsequence whose elements sum to $K$? Find the count modulo $998244353$.

Here, “to divide $A$ into several contiguous subsequences” means the following procedure.

  • Freely choose the number $k$ $(1 \leq k \leq N)$ of subsequences and an integer sequence $(i_1, i_2, \dots, i_k, i_{k+1})$ satisfying $1 = i_1 \lt i_2 \lt \dots \lt i_k \lt i_{k+1} = N+1$.
  • For each $1 \leq n \leq k$, the $n$-th subsequence is formed by taking the $i_n$-th through $(i_{n+1} - 1)$-th elements of $A$, maintaining their order.

Here are some examples of divisions for $A = (1, 2, 3, 4, 5)$:

  • $(1, 2, 3), (4), (5)$
  • $(1, 2), (3, 4, 5)$
  • $(1, 2, 3, 4, 5)$
#include <bits/stdc++.h>
#define fi first
#define se second
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int mod = 998244353;

signed main() {
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int n, k;
    cin >> n >> k;

    vector<int> a(n + 1), s(n + 1, 0);
    for (int i = 1; i <= n; i ++)
        cin >> a[i], s[i] = s[i - 1] + a[i];

    vector<int> dp(n + 1, 0);
    unordered_map<int, int> tot;
    dp[0] = 1, tot[0] = 1;
    int sum = 1;
    for (int i = 1; i <= n; i ++) {
        dp[i] = (sum - tot[s[i] - k] + mod) % mod;
        (sum += dp[i]) %= mod, (tot[s[i]] += dp[i]) %= mod;
    }

    cout << dp[n] << endl;

    return 0;
}
阅读全文

牛客小白月赛100 E

题解 2024/9/6

E-ACM中的CM题

现在在你前面有 nnn 个地雷

刚刚学会瞬移的你决定排走所有地雷

遗憾的是,你的初始排雷能力 m=0m = 0m=0,但你知道

当排雷能力为 mmm 时,你可以任何时刻在当前位置 lll 处将[l,l+m][l, l+m][l,l+m] 中的雷全部排走

也可以在任何时刻任何地点,将排雷能力从 mmm 变为 m+1m+1m+1

以上两种操作都需要花费 1 的时间,但瞬移不花费时间!

求排所有雷所需最小时间!

思路

枚举m长度二分求次数即可

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int a[N];
int n, ans;
void check(int len) {
    int res = len - 1;
    for (int i = 1; i <= n; i++) {
        int l = i, r = n;
        while (l < r) {
            int mid = (l + r + 1) / 2;
            if (a[mid] - a[i] + 1 > len)
                r = mid - 1;
            else
                l = mid;
        }
        res++;
        i = l;
        if (l == n)
            break;
    }
    ans = min(ans, res);
}

void solve() {
    int num;
    cin >> num;
    n = 0;
    map<int, int> mp;
    for (int i = 1; i <= num; i++) {
        int x;
        cin >> x;
        if (!mp[x]) {
            a[++n] = x;
        }
        mp[x] = 1;
    }
    ans = n;
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i++) {
        check(i);
    }
    cout << ans << endl;
}
signed main() {
    int t = 1;
    while (t--) {
        solve();
    }
    return 0;
}
阅读全文

线段树板子题《最大数》

算法 2024/9/6

[JSOI2008] 最大数

题目描述

现在请求你维护一个数列,要求提供以下两种操作:

  1. 查询操作。

语法:Q L

功能:查询当前数列中末尾 $L$ 个数中的最大的数,并输出这个数的值。

限制:$L$ 不超过当前数列的长度。$(L > 0)$

  1. 插入操作。

语法:A n

功能:将 $n$ 加上 $t$,其中 $t$ 是最近一次查询操作的答案(如果还未执行过查询操作,则 $t=0$),并将所得结果对一个固定的常数 $D$ 取模,将所得答案插入到数列的末尾。

限制:$n$ 是整数(可能为负数)并且在长整范围内。

注意:初始时数列是空的,没有一个数。

输入格式

第一行两个整数,$M$ 和 $D$,其中 $M$ 表示操作的个数,$D$ 如上文中所述。

接下来的 $M$ 行,每行一个字符串,描述一个具体的操作。语法如上文所述。

输出格式

对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

样例 #1

样例输入 #1

5 100
A 96
Q 1
A 97
Q 1
Q 2

样例输出 #1

96
93
96

提示

数据规模与约定

对于全部的测试点,保证 $1 \leq M \leq 2 \times 10^5$,$1 \leq D \leq 2 \times 10^9$。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7; 
struct ty
{
    int minv;
}tree[4*N];
void update(int id)
{
    tree[id].minv=max(tree[id*2].minv,tree[id*2+1].minv);
}
int query(int id,int l,int r,int ql,int qr)
{
    if(l==ql&&r==qr) return tree[id].minv;
    int mid=(l+r)>>1;
    if(qr<=mid) return query(id*2,l,mid,ql,qr);
    else if(ql>mid) return query(id*2+1,mid+1,r,ql,qr);
    else return max(query(id*2,l,mid,ql,mid),query(id*2+1,mid+1,r,mid+1,qr));
}
void change(int id,int l,int r,int pos,int val)
{
    if(l==r) tree[id].minv=val;
    else
    {
        int mid=(l+r)>>1;
        if(pos<=mid) change(id*2,l,mid,pos,val);
        else change(id*2+1,mid+1,r,pos,val);
        update(id);
    }
}
void solve()
{
    int n,m;
    cin>>n>>m;

    //build(1,1,n);
    int last=0;
    int idx=0;
    for(int i=1;i<=n;i++)
    {
        char op;
        cin>>op;
        if(op=='A') 
        {
            int x;
            cin>>x;
            change(1,1,n,idx+1,(x+last)%m);
            idx++;
        }
        else
        {
            int x;
            cin>>x;
            last=query(1,1,n,idx-x+1,idx);
            cout<<last<<endl;
        }

    }
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
struct tr
{
    int l,r;
    int v;
}tree[4*N];
void build(int id,int l,int r)
{
    tree[id]={l,r};
    if(l==r) return;
    int mid=(l+r)>>1;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
}
void update(int id)
{
    tree[id].v=max(tree[id*2].v,tree[id*2+1].v);
}
void change(int id,int pos,int val)
{
    if(tree[id].r==tree[id].l) tree[id].v=val;
    else
    {
        int mid=(tree[id].l+tree[id].r)>>1;
        if(pos<=mid) change(id*2,pos,val);
        else change(id*2+1,pos,val);
        update(id);
    }
}
int query(int id,int ql,int qr)
{
    if(tree[id].l==ql&&tree[id].r==qr) return tree[id].v;
    int mid=tree[id].l+tree[id].r>>1;
    if(qr<=mid)return query(id*2,ql,qr);
    else if(ql>mid) return query(id*2+1,ql,qr);
    else return max(query(id*2,ql,mid),query(id*2+1,mid+1,qr));
}
void solve()
{
    int n,m;
    cin>>n>>m;
    int idx=0,last=0;
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        char op;
        int x;
        cin>>op>>x;
        if(op=='A')
        {
            change(1,idx+1,(x+last)%m);
            idx++;
        }
        else
        {
            last=query(1,idx-x+1,idx);
            cout<<last<<endl;
        }
    }
}
signed main()
{
    int t=1;
    while(t--)
    {
        solve();
    }
}
阅读全文

Codeforces Round 970 (Div. 3)

题解 2024/9/2

A. Sakurako’s Exam

题目

Today, Sakurako has a math exam. The teacher gave the array, consisting of $a$ ones and $b$ twos.

In an array, Sakurako must place either a ‘+’ or a ‘-‘ in front of each element so that the sum of all elements in the array equals $0$.

Sakurako is not sure if it is possible to solve this problem, so determine whether there is a way to assign signs such that the sum of all elements in the array equals $0$.

实现


  #include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7; 
void solve()
{
    int a,b;
    cin>>a>>b;
    int now=a+b*2;
    while(b>0 and now>=4)
    {
        b--;
        now-=4;
    }
    while(a>0 and now>=2)
    {
        a--;
        now-=2;
    }
    if(now==0) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}

B. Square or Not

题目

A beautiful binary matrix is a matrix that has ones on its edges and zeros inside.

Today, Sakurako was playing with a beautiful binary matrix of size $r \times c$ and created a binary string $s$ by writing down all the rows of the matrix, starting from the first and ending with the $r$-th. More formally, the element from the matrix in the $i$-th row and $j$-th column corresponds to the $((i-1)*c+j)$-th element of the string.

You need to check whether the beautiful matrix from which the string $s$ was obtained could be squared. In other words, you need to check whether the string $s$ could have been build from a square beautiful binary matrix (i.e., one where $r=c$).

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
void solve()
{
   int n;
   cin>>n;
   string s;
   cin>>s;
   if(sqrt(n)*sqrt(n)!=n) 
   {
        cout<<"No"<<endl;
        return;
   }
   s=' '+s;   
   if(n==4||s==" 1111")
   {
        cout<<"Yes"<<endl;
        return;
   }
   int num=sqrt(n);
   for(int i=num+1;i<=n-num;i++)
   {
        if((i%num==1||i%num==0)&&s[i]=='0') 
        {
            cout<<"No"<<endl;
            return;
        }
        if((i%num)!=1&&((i%num)!=0))
        {
            if(s[i]=='1')
            {
                cout<<"No"<<endl;
                return;
            }
        }
   }
   cout<<"Yes"<<endl;
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}

C. Longest Good Array

题目

Today, Sakurako was studying arrays. An array $a$ of length $n$ is considered good if and only if:

  • the array $a$ is increasing, meaning $a_{i - 1} < a_i$ for all $2 \le i \le n$;
  • the differences between adjacent elements are increasing, meaning $a_i - a_{i-1} < a_{i+1} - a_i$ for all $2 \le i < n$.

Sakurako has come up with boundaries $l$ and $r$ and wants to construct a good array of maximum length, where $l \le a_i \le r$ for all $a_i$.

Help Sakurako find the maximum length of a good array for the given $l$ and $r$.

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e3;
const int M=2e5+10;
void solve()
{
   int n;
   cin>>n;
   for(int i=1;i<=n;i++)
   {
        int l,r;
        cin>>l>>r;
        int num=0;
        for(;num*(num+1)/2<=r-l;num++)
        {
            ;
        }
        if(num*(num+1)/2>r-l) num--;
        cout<<num+1<<endl;
   }
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}

D. Sakurako’s Hobby

题目

For a certain permutation $p$$^{\text{∗}}$ Sakurako calls an integer $j$ reachable from an integer $i$ if it is possible to make $i$ equal to $j$ by assigning $i=p_i$ a certain number of times.

If $p=[3,5,6,1,2,4]$, then, for example, $4$ is reachable from $1$, because: $i=1$ $\rightarrow$ $i=p_1=3$ $\rightarrow$ $i=p_3=6$ $\rightarrow$ $i=p_6=4$. Now $i=4$, so $4$ is reachable from $1$.

Each number in the permutation is colored either black or white.

Sakurako defines the function $F(i)$ as the number of black integers that are reachable from $i$.

Sakurako is interested in $F(i)$ for each $1\le i\le n$, but calculating all values becomes very difficult, so she asks you, as her good friend, to compute this.

$^{\text{∗}}$A permutation of length $n$ is an array consisting of $n$ distinct integers from $1$ to $n$ in arbitrary order. For example, $[2,3,1,5,4]$ is a permutation, but $[1,2,2]$ is not a permutation (the number $2$ appears twice in the array), and $[1,3,4]$ is also not a permutation ($n=3$, but the array contains $4$).

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
//const int M=2e5+10;
int dp[N];
int a[N],b[N];
int p[N];
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
void solve()
{
   int n;
   cin>>n;
   for(int i=1;i<=n;i++)
   {
        cin>>a[i];
        p[i]=i;
   }
   string s;
   cin>>s;
   s=' '+s;
    for(int i=1;i<=n;i++)
    {
        p[find(a[i])]=find(i);
    }
    map<int,int>mp;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='0') mp[find(i)]++;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<mp[find(i)]<<' ';
    }
    cout<<endl;
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}

E. Alternating String

题目

Sakurako really loves alternating strings. She calls a string $s$ of lowercase Latin letters an alternating string if characters in the even positions are the same, if characters in the odd positions are the same, and the length of the string is even.

For example, the strings ‘abab’ and ‘gg’ are alternating, while the strings ‘aba’ and ‘ggwp’ are not.

As a good friend, you decided to gift such a string, but you couldn’t find one. Luckily, you can perform two types of operations on the string:

  1. Choose an index $i$ and delete the $i$-th character from the string, which will reduce the length of the string by $1$. This type of operation can be performed no more than $1$ time;
  2. Choose an index $i$ and replace $s_i$ with any other letter.

Since you are in a hurry, you need to determine the minimum number of operations required to make the string an alternating one.

实现

考虑两种状态,一种n为偶数,一种为奇数,偶数必不进行删除操作,那只需要贪心选最小成本即可,奇数要枚举删除的位置,删完之后的位置奇偶的顺序会发生变化,因此记录一下i号位当前的各字母出现情况,注意区分奇偶序列,从1到n枚举,每次贪心选出最后剩什么字母的成本,取最小即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
//const int M=2e5+10;
int dp[N];
int a[N],b[N];
int ch1[N][27];
int ch2[N][27];
void solve()
{
    int n;
    cin>>n;
    string s;
    cin>>s;
    s=' '+s;
    map<char,int>mp1;
    map<char,int>mp2;
    if(n%2==0)
    {
        for(int i=1;i<=n;i++)
        {
            if(i%2==1) mp1[s[i]]++;
            else mp2[s[i]]++;
        }
        int ans1=0;
        int ans2=0;
        for(auto it:mp1) ans1=max(ans1,it.second);
        for(auto it:mp2) ans2=max(ans2,it.second);
        cout<<n-ans1-ans2<<endl;
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=26;j++)
            {
                ch1[i][j]=0;
                ch2[i][j]=0;
            }
            if(i%2==1)
            {
                ch1[i][s[i]-'a']++;
                for(int j=0;j<26;j++)
                {
                    ch1[i][j]+=ch1[i-1][j];
                    ch2[i][j]+=ch2[i-1][j];
                }
            }
            else
            {
                ch2[i][s[i]-'a']++;
                for(int j=0;j<26;j++)
                {
                    ch1[i][j]+=ch1[i-1][j];
                    ch2[i][j]+=ch2[i-1][j];
                }
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            int max1=0;
            int max2=0;
            for(int j=0;j<26;j++)
            {
                max1=max(ch2[n][j]-ch2[i][j]+ch1[i-1][j],max1);
                max2=max(ch1[n][j]-ch1[i][j]+ch2[i-1][j],max2);
            }
            ans=max(ans,max1+max2);
        }
        cout<<n-ans<<endl;
    }
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}

F. Sakurako’s Box

题目

Sakurako has a box with $n$ balls. Each ball has it’s value. She wants to bet with her friend that if the friend randomly picks two balls from the box (it could be two distinct balls, but they may have the same value), the product of their values will be the same as the number that Sakurako guessed.

Since Sakurako has a PhD in probability, she knows that the best number to pick is the expected value, but she forgot how to calculate it. Help Sakurako and find the expected value of the product of two elements from the array.

It can be shown that the expected value has the form $\frac{P}{Q}$, where $P$ and $Q$ are non-negative integers, and $Q \ne 0$. Report the value of $P \cdot Q^{-1}(\bmod 10^9+7)$.

实现

我恨取模

image-20240903002706846

算贡献+组合数+逆元

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
int dp[N];
int a[N],b[N];
const int mod=1e9+7; 
int quick_pow(int a,int b)
{
    int ans=1;
    while(b){
        if(b&1) ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}
int inv(int a,int b)
{
    return (a*quick_pow(b,mod-2))%mod;
}
void solve()
{
    int n;
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i],sum%=mod;
    int num=(n-1)*n/2;
    num%=mod;
    int d1=0;
    for(int i=1;i<=n;i++)
    {
        sum-=a[i];
        d1+=(a[i]%mod)*((sum+mod)%mod);
        d1%=mod;
    }
    int ans=inv(d1,num);
    cout<<(ans+mod)%mod<<endl;
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}

G. Sakurako’s Task

题目

Sakurako has prepared a task for you:

She gives you an array of $n$ integers and allows you to choose $i$ and $j$ such that $i \neq j$ and $a_i \ge a_j$, and then assign $a_i = a_i - a_j$ or $a_i = a_i + a_j$. You can perform this operation any number of times for any $i$ and $j$, as long as they satisfy the conditions.

Sakurako asks you what is the maximum possible value of $mex_k$$^{\text{∗}}$ of the array after any number of operations.

$^{\text{∗}}$$mex_k$ is the $k$-th non-negative integer that is absent in the array. For example, $mex_1({1,2,3 })=0$, since $0$ is the first element that is not in the array, and $mex_2({0,2,4 })=3$, since $3$ is the second element that is not in the array.

实现

尽可能让所有的数字变小就是最优解,手动模拟一下会发现最后的数组排列为i*数组最大公因数(0<=i<=n)

将k按顺序插入即是最答案

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7; 
int a[N];
int gcd(int a,int b)
{
    if(a%b==0) return b;
    else return gcd(b,a%b);
}
void solve()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int num=0;
    for(int i=1;i<=n;i++)
    {
        if(!num) num=a[i];
        else num=gcd(num,a[i]);
    }
    if(n == 1)
    {
       if(k>a[1]) cout<<k<<endl;
       else cout<<k-1<<endl;
    }
    else
    {
        if(num==1) cout<<n-1+k<<endl;
        else
        {
            int sum=((k+num-2)/(num-1));
            sum=min(sum,n);
            cout<<k+sum-1<<endl;
        }
    }
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
}
阅读全文

dijkstra求最短路(多源版本)

算法 2024/9/1
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e3;
int u[N],v[N];
int dist[N][N];
vector<int>e[N];
bool st[N][N];
typedef pair<int,int> PII;
void dijkstra(int root)
{
    priority_queue<PII, vector<PII>, greater<PII>>heap;
    dist[root][root]=0;
    heap.push({0,root});
    while(heap.size())
    {
        auto k=heap.top();
        heap.pop();
        int ver=k.second;
        int distence=k.first;
        if(st[root][ver]) continue;
        st[root][ver]=1;
        for(int i=0;i<e[ver].size();i++)
        {
            if(dist[root][e[ver][i]]>=distence+dist[ver][e[ver][i]])
            {
                dist[root][e[ver][i]]=distence+dist[ver][e[ver][i]];
                heap.push({dist[root][e[ver][i]],e[ver][i]});
            }
        }
    }
}
void solve()
{
    int n,m;
    cin>>n>>m;
    memset(dist,127,sizeof(dist));
    for(int i=1;i<=m;i++)
    {
        int w;
        cin>>u[i]>>v[i]>>w;
        if(!st[u[i]][v[i]])
        {
            st[u[i]][v[i]]=1;
            st[v[i]][u[i]]=1;
            e[u[i]].push_back(v[i]);
            e[v[i]].push_back(u[i]);
        }
        dist[u[i]][v[i]]=min(dist[u[i]][v[i]],w);
        dist[v[i]][u[i]]=min(dist[v[i]][u[i]],w);
    }
    memset(st,0,sizeof(st));
    for(int i=1;i<=n;i++)
    {
        dijkstra(i);
    }
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}
阅读全文
1 ... 2 3
avatar
joso

齐鲁工业大学(山东省科学院)