ARC169

[ARC169A] Please Sign#

每个点会一直一直给它的父节点加,所以深度越深的点影响越大,统计出每个深度的点权和,从深到浅判断正负,有正负就输出答案,全都没有就是 0

Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=3e5+10;
int n,a[N],dep[N],sum[N];
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
signed main(){
    n=read();
    for(re int i=1;i<=n;i++)a[i]=read();
    sum[0]=a[1];
    for(re int i=2;i<=n;i++)
        sum[dep[i]=dep[read()]+1]+=a[i];
    for(re int i=n;i>=0;i--)
        if(sum[i])return putchar(sum[i]>0?'+':'-'),0;
    putchar('0');
    return 0;
}

[ARC169B] Subsegments with Small Sums#

想起了 ARC060E。考虑倍增。

然后套一个 dpfi 表示从任意起点走到 i 的方案总和。

发现倍增没有用,还会算重,所以换回初始的东西,就是预处理出 nxti 表示 i 跳一步最远跳到哪儿。

对于每一组 [i,nxti] 我们从 i 跳到 [i,n] 中的任意一个位置都会用到它,所以 i 位置的贡献是 (ni+1)fi1,然后转移一下 ffnxti=fnxti+fi1

我也很震惊竟然直接就过了,开心。

Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=3e5+10;
int n,S,a[N],nxt[N],f[N],s[N],ans;
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    n=read(),S=read();
    for(re int i=1;i<=n;i++)a[i]=read(),s[i]=s[i-1]+a[i];
    for(re int i=1;i<=n;i++)
        nxt[i]=upper_bound(s+1,s+1+n,s[i-1]+S)-s-1;
    for(re int i=0;i<n;i++)f[i]=1;
    for(re int i=1;i<=n;i++){
        ans+=(n-i+1)*f[i-1];
        f[nxt[i]]+=f[i-1];
    }
    cout<<ans;
    return 0;
}

[ARC169C] Not So Consecutive#

像一般的线性 dp 按每一位考虑是不容易的,因为需要知道这一位填什么,已经连续填了多少个,状态数就是 O(n3) 的。当然好像也能做,但我没发现第一篇题解的那个队列+维护和,感觉可能是因为没有认真地把式子写下来导致的。

考虑按连续段转移,设 fi,j 表示填了前 i 个位置,最后填了一个颜色为 j 的连续段。

枚举连续段的起点 k,这里需要满足 [k,i] 里面没有已经被确定为其他数(j)了的。这个我们可以记录一个 pos 存每个颜色最后出现的位置,每次到了一个被确定的位置就更新,然后对所有 pos 求最大值 mx1 和次大值 mx2,如果 amx1=j,那么那么 k 最远可以到 mx2,否则可以到 mx1,注意为了满足题目的限制还要对 ij 取一个 max,然后把这个最远可以取到的位置记为 lst

转移就比较简单:

fi,j=k=posi1coljfk,col

colj 容斥掉:

fi,j=k=posi1col=1nfk,colk=posi1fk,j

O(n4),于是发现这两部分都可以用前缀和优化掉,O(n2)

Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=5010,mod=998244353;
int n,a[N],pos[N],f[N][N],s[N][N],S[N];
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    n=read();
    for(re int i=1;i<=n;i++)a[i]=read();
    f[0][0]=S[0]=1;
    for(re int i=1;i<=n;i++){
        if(a[i]!=-1)pos[a[i]]=i;
        int mx1=0,mx2=0;
        for(re int j=1;j<=n;j++)
            if(pos[j]>mx1)mx2=mx1,mx1=pos[j];
            else mx2=max(mx2,pos[j]);
        for(re int j=1;j<=n;j++){
            int lst=max(i-j,(a[mx1]==j)?mx2:mx1);
            f[i][j]=(S[i-1]-(lst?S[lst-1]:0)+mod-(s[i-1][j]-(lst?s[lst-1][j]:0)+mod)+mod)%mod;
            s[i][j]=(s[i-1][j]+f[i][j])%mod,S[i]+=f[i][j];
        }
        S[i]=(S[i]+S[i-1])%mod;
    }
    cout<<(S[n]-S[n-1]+mod)%mod;
    return 0;
}

[ARC169D] Add to Make a Permutation#

很符合我对做思维题的感觉的期望。

显然这些操作就是每次选 m 个数加一,最后全部 modn。先省去取模这一步,记最后得到的未取模的序列为 b。那么 b 需要满足以下条件:

  1. i[1,n],biai

  2. bimodn 之后两两不相同;

  3. sum=i=1n(biai)m|sum

  4. mx=maxi=1n{biai}mxsumm

考虑第四个条件,当操作数一定时,mx 应尽量小才更有可能满足条件,又因为此时 sum 一定,所以每个位置加的量应该尽量“均匀分配”。即我们把 a 升序排序,那么操作完之后的 b 序列也应是升序的,所以下面钦定 a,b 都是升序的。

结论:若有解,那么一定存在一种最优解形如 b={x,x+1,x+2,,x+n1}

用调整法证明。假设我们得到了一个最优解 b,且 bnb1n,那么我们重新构造这两项:b1=bnn,bn=b1+n。然后我们来 check 一下是否仍然满足条件:

  1. bnb1+n,b1a1,b1=bnnb1a1a1+n>an,b1a1,bn=b1+na1+n>an

  2. 加减 n 不影响 modn 之后的结果;

  3. 加减 n 之后 sum 不变,又因为答案只与 sum 有关,所以答案不变,仍然最优;

  4. bn=b1+nbn,bnanbnanbnanbn(a1+n)=bnna1=b1a1

综上,调整之后的序列仍然合法。

那么考虑计算最优解,即最小化 x 的值。

第一个条件为 x 提供了一个下界 maxi=1n{ai(i1)}
再看第四个条件,x 没增加 1,不等式左边增加 1,右边增加 nm,又因为 m<n,所以 nm>1,因此它也为 x 提供了一个下界。

最后只需要满足第三个条件就好了,发现 xx+m 对这个条件而言是等价的,并且后者答案更劣。所以只需从 x 开始往上枚举 m 次到 x+m1 即可。

Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=3e5+10;
int n,m,a[N],x,sum,mx;
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
signed main(){
    n=read(),m=read();
    for(re int i=0;i<n;i++)a[i]=read();
    sort(a,a+n);
    for(re int i=0;i<n;i++)x=max(x,a[i]-i);
    for(re int i=0;i<n;i++)sum+=x+i-a[i];
    bool flg=0;
    for(re int i=0;i<m;i++)
        if(!((sum+n*i)%m)){
            flg=1,x+=i,sum+=n*i;
            break;
        }
    if(!flg)return puts("-1"),0;
    for(re int i=0;i<n;i++)mx=max(mx,x+i-a[i]);
    while(mx>sum/m)mx+=m/__gcd(n,m),sum+=n*m/__gcd(n,m);
    cout<<sum/m;
    return 0;
}

[ARC169E] Avoid Boring Matches#

先判无解,RB 多显然无解,否则排成 BBB...RRR 一定有解。

考虑每轮匹配的最优策略,我们首先保证留下尽量多的 B,其次保证留下的这些 B 的位置尽量靠前。那么我们从左往右考虑每一个 B,将它与它后面第一个 R 匹配,最后再把没有匹配的随便匹配。

考虑一个合法串“至少”应该是怎样,即找到一个“合法的最低标准”,那么我们考虑在构造合法解的同时让 B 的数量尽量少(刚好一半),并且位置尽量靠后。

ti 表示长度为 2i 的“最低合法标准”串。令 t0=R

考虑怎样由 ti1 得到 ti,那么依次考虑 ti1 的每一位:如果为 R 说明没有匹配,就在 ti 后面接上 R;如果为 B,那么我们要使得下一个 B 尽量靠后,就要在此处多放一个 R 直接与它配对,这样就可以多占一个位置,使得下一个 B 尽量靠后,即在 ti 后面接上 BR

然后考虑怎样让 s 达到最低合法标准 tn。设 tn 中第 iB 的位置为 Ti,类似地有 Si。如果存在 Si>Ti,说明这里 s 没有达到最低标准,就得把它往前面 swap SiTi 次使得刚好达到最低标准。

于是就得到了最少 swap 的次数。

Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define re register
const int N=19;
string s,t[N];
int n,tot,pos[1<<N];
ll ans;
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
int main(){
    n=read(),getline(cin,s);
    t[0]="R";
    for(re int i=1;i<=n;i++){
        for(char c:t[i-1])
            if(c=='R')t[i]+='R';
            else t[i]+="BR";
        int d=(1<<i)-t[i].size();
        while(d--)t[i]+='B';
    }
    for(re int i=0;i<(1<<n);i++)
        if(t[n][i]=='B')pos[++tot]=i;
    tot=0;
    for(re int i=0;i<(1<<n);i++)
        if(s[i]=='B'){
            ans+=max(0,i-pos[++tot]);
            if(tot==(1<<n-1))break;
        }
    if(tot^(1<<n-1))puts("-1");
    else cout<<ans;
    return 0;
}
posted @   MrcFrst  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
主题色彩