[游记]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