其他分享
首页 > 其他分享> > [游记]2022年多校冲刺NOIP联训测试8-2022.7.30

[游记]2022年多校冲刺NOIP联训测试8-2022.7.30

作者:互联网

这次好像也不错qwq

A. 序列

B. 任意模数快速插值

C. 快递

D. 任意模数多项式乘法逆

A. 序列

一眼题面:这不是在模拟更相减损么

然后发现的确是,所以飞快地过了

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1001000;
int n,ans;
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
int gcd(int x,int y){
    if(!y) return x;
    return gcd(y,x%y);
}
signed main(){
    n=read();
    ans=read();
    for(int i=2;i<=n;i++){
        int tmp=read();
        ans=gcd(ans,tmp);
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

 

 

B. 任意模数快速插值

这,这名字是唬人的吧(

这种套路做的很多了,直接用单调栈维护出一个数字的支配区间,然后分开处理就行了

也可以用线段树

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1001000,mod=998244353;
int n,a[WR];
int ans;
int maxx[WR],minn[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void CDQ(int l,int r){
    if(l==r){
        ans=(ans+a[l]*a[l]%mod)%mod;
        return;
    }
    int mid=(l+r)>>1;
    CDQ(l,mid);
    CDQ(mid+1,r);
    maxx[mid]=minn[mid]=a[mid];
    maxx[mid+1]=minn[mid+1]=a[mid+1];
    for(int i=mid-1;i>=l;i--){
        maxx[i]=max(maxx[i+1],a[i]);
        minn[i]=min(minn[i+1],a[i]);
    }
    for(int i=mid+2;i<=r;i++){
        maxx[i]=max(maxx[i-1],a[i]);
        minn[i]=min(minn[i-1],a[i]);
    }
    int st,ed,res;
    ed=mid;
    for(int i=mid;i>=l;i--){
        while(ed+1<=r&&maxx[i]>=maxx[ed+1]&&minn[i]<=minn[ed+1]) ed++;
        ans=(ans+(maxx[i]*minn[i])%mod*(ed-mid)%mod)%mod;
    }
    st=mid+1;
    for(int i=mid+1;i<=r;i++){
        while(st-1>=l&&maxx[i]>maxx[st-1]&&minn[i]<minn[st-1]) st--;
        ans=(ans+(maxx[i]*minn[i])%mod*(mid-st+1)%mod)%mod;
    }
    st=mid+1,ed=mid;res=0;
    for(int i=mid;i>=l;i--){
        while(ed+1<=r&&maxx[i]>=maxx[ed+1]) ed++,res=(res+minn[ed])%mod;
        while(st<=ed&&minn[i]<=minn[st]) res=(res-minn[st]+mod)%mod,st++;
        ans=(ans+res*maxx[i]%mod)%mod;
    }
    st=mid+1,ed=mid;res=0;  
    for(int i=mid+1;i<=r;i++){
        while(st-1>=l&&maxx[i]>maxx[st-1]) st--,res=(res+minn[st])%mod;
        while(ed>=st&&minn[i]<minn[ed]) res=(res-minn[ed]+mod)%mod,ed--;
        ans=(ans+res*maxx[i]%mod)%mod;
    }
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    CDQ(1,n);
    printf("%lld\n",ans);
    return 0;
}
单调栈解法

 

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int maxn = 510005;
LL a[maxn];
int st1[maxn], tp1;
int st2[maxn], tp2;
struct tree
{
    int l, r;
    int len;
    int z1, z2, suml, sumr, val;
} t[maxn * 4];
 
void build(int p, int l, int r)
{
    t[p].l = l, t[p].r = r, t[p].len = r - l + 1;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    build(p * 2, l, mid);
    build(p * 2 + 1, mid + 1, r);
}
 
void pushdown(int p)
{
    if (t[p].z1)
    {
        t[p * 2].z1 = t[p * 2 + 1].z1 = t[p].z1;
        t[p * 2].suml = 1ll*t[p * 2].len * t[p].z1 % mod;
        t[p * 2 + 1].suml = 1ll*t[p * 2 + 1].len * t[p].z1 % mod;
        t[p * 2].val = 1ll*t[p * 2].z1 * t[p * 2].sumr % mod;
        t[p * 2 + 1].val = 1ll*t[p * 2 + 1].z1 * t[p * 2 + 1].sumr % mod;
        t[p].z1 = 0;
    }
    if (t[p].z2)
    {
        t[p * 2].z2 = t[p * 2 + 1].z2 = t[p].z2;
        t[p * 2].sumr = 1ll*t[p * 2].len * t[p].z2 % mod;
        t[p * 2 + 1].sumr = 1ll*t[p * 2 + 1].len * t[p].z2 % mod;
        t[p * 2].val = 1ll*t[p * 2].suml * t[p * 2].z2 % mod;
        t[p * 2 + 1].val =1ll* t[p * 2 + 1].suml * t[p * 2 + 1].z2 % mod;
        t[p].z2 = 0;
    }
}
 
void changel(int p, int l, int r, LL w)
{
    if (l <= t[p].l && t[p].r <= r)
    {
        t[p].suml = 1ll*t[p].len * w % mod;
        t[p].val = 1ll*w * t[p].sumr % mod;
        t[p].z1 = w;
        return;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (l <= mid)
        changel(p * 2, l, r, w);
    if (r > mid)
        changel(p * 2 + 1, l, r, w);
    t[p].val = 1ll*(t[p * 2].val + t[p * 2 + 1].val) % mod;
    t[p].suml = 1ll*(t[p * 2].suml + t[p * 2 + 1].suml) % mod;
    t[p].sumr = 1ll*(t[p * 2].sumr + t[p * 2 + 1].sumr) % mod;
}
 
void changer(int p, int l, int r, int w)
{
    if (l <= t[p].l && t[p].r <= r)
    {
        t[p].sumr = 1ll*t[p].len * w % mod;
        t[p].val = 1ll*t[p].suml * w % mod;
        t[p].z2 = w;
        return;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (l <= mid)
        changer(p * 2, l, r, w);
    if (r > mid)
        changer(p * 2 + 1, l, r, w);
    t[p].val = 1ll*(t[p * 2].val + t[p * 2 + 1].val) % mod;
    t[p].suml = 1ll*(t[p * 2].suml + t[p * 2 + 1].suml) % mod;
    t[p].sumr = 1ll*(t[p * 2].sumr + t[p * 2 + 1].sumr) % mod;
}
int l[maxn], r[maxn];
int main()
{
    int n;
    cin >> n;
 
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    build(1, 1, n);
    LL ans = 0;
    for (int i = 1; i <= n; i++)
    {
        while (a[i] >= a[st1[tp1]] && tp1)
            tp1--;
        while (a[i] <= a[st2[tp2]] && tp2)
            tp2--;
        changel(1, st1[tp1] + 1, i, a[i]);
        changer(1, st2[tp2] + 1, i, a[i]);
        LL sum = t[1].val;
        //cout<<t[1].suml<<endl;
        ans += sum;
        ans %= mod;
        st1[++tp1] = i;
        st2[++tp2] = i;
        /*for(int i=1;i<=4*n;i++){
            cout<<t[i].l<<" "<<t[i].r<<" "<<t[i].suml<<" "<<t[i].sumr<<" "<<t[i].z1<<" "<<t[i].z1<<" "<<t[i].val<<endl;
        }
        cout<<endl;*/
    }
    cout << ans;
    return 0;
}
artalter的线段树做法

 

 

 

C. 快递

什么诡异的图上三进制状态压缩 $\operatorname{DP}\cdots$

首先注意到诡异的数据范围,大概率要状压

然后发现有重边,考虑一个邻接矩阵存图, $\operatorname{Floyed}$ 预处理多源最短路

发现每个快递不只有选和不选两种状态,还有一个“在配送”的奇妙状态,状压变成了三进制

由于是三进制,考虑一个 $base$ 的预处理,处理出 $3$ 的幂次方方便计算

枚举状态 $S$ ,如果当前快递还没有选,看一下选的话会不会更优

用当前快递的开始配送时间 $l[i]$ 和从当前位置走到快递点的时间取最大,进行更新

如果当前快递已经选择过了,显然是要直接把它送到

用类似于 $\operatorname{Floyed}$ 的思想,以当前节点为更新点考虑能否更新即可

统计答案时,如果当前的这一位是 $2$ 那么表示快递被送到了, $cnt++$

最后的答案就是 $cnt$ 的最大值

时间复杂度是离谱的 $\Theta(3^qnq)$ ,但注意到玄学的数据范围所以可过

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=200100;
struct Task{
    int s,t,l,r;
}task[WR];
int n,m,q;
int edge[25][25];
int dis[25][25];
int dp[WR][21];
int base[30];
int ans;
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void Floyed(){
    for(int i=1;i<=n;i++) dis[i][i]=0;
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
            }
        }
    }
}
signed main(){
    n=read(),m=read(),q=read();
    memset(dis,0x3f,sizeof(dis));
    base[0]=1;
    for(int i=1;i<=q;i++) base[i]=base[i-1]*3;
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),val=read();
        dis[u][v]=min(dis[u][v],val);
    }
    Floyed();
    // for(int i=1;i<=n;i++){
    //     for(int j=1;j<=n;j++){
    //         printf("%lld ",dis[i][j]);
    //     }
    //     printf("\n");
    // }
    memset(dp,0x3f,sizeof(dp));
    dp[0][1]=0;
    for(int i=0;i<q;i++){
        task[i].s=read(),task[i].t=read();
        task[i].l=read(),task[i].r=read();
    }
    for(int S=0;S<base[q];S++){
        for(int i=1;i<=n;i++){
            for(int j=0;j<q;j++){
                int opt=(S/base[j])%3;
                if(!opt){
                    dp[S+base[j]][task[j].s]=min(dp[S+base[j]][task[j].s],
                                                max(dp[S][i]+dis[i][task[j].s],task[j].l));
                }
                if(opt==1&&dp[S][i]+dis[i][task[j].t]<=task[j].r){
                    dp[S+base[j]][task[j].t]=min(dp[S+base[j]][task[j].t],
                                                dp[S][i]+dis[i][task[j].t]);
                }
            }
            if(dp[S][i]!=dp[0][0]){
                int cnt=0;
                for(int j=0;j<=q;j++){
                    if((S/base[j])%3==2) cnt++;
                }
                ans=max(ans,cnt);
            }
        }
    }
    // for(int i=0;i<=base[q]-1;i++){
    //     for(int j=1;j<=n;j++){
    //         if(dp[i][j]==dp[0][0]) printf("- ");
    //         else printf("%lld ",dp[i][j]);
    //     }
    //     printf("\n");
    // }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

 

 

 D. 任意模数多项式乘法逆

 

标签:ch,NOIP,int,30,mid,1ll,联训,WR,mod
来源: https://www.cnblogs.com/WintersRain/p/16538376.html