8.16 普及模拟一
噔 噔 咚
快读锅了 结果全部T掉QAQ (下面所说的得分是指赛后将快读改成scanf后的得分)
\(\large{T1\ Past} \ \ \tiny{40 pst}\)
- 提意:求数列的所有子区间的和之和、极差之和、平均数之和。
- 考场乱胡了个线段树就run了
- 这题只能用\(O(n)\)的算法
- 子区间和之和:可以计算每个元素在每个子区间的出现次数,由打表可知:设数列长度为\(n\),元素在数列中的位置为\(i\) 则\(元素出现次数=i\times(n-i+1)\)
- 极差之和:用单调栈分别维护元素作为最大值的区间和元素作为最小值的区间。
- 坑点:模之后可能会减出负数,需要加减运算时加模数。
- 平均数之和:由打表可知:设区间长度为\(l\),数列中的元素为\(a_i\),当区间为\(l\)时的区间总和为\(sum_l\),则\(sum_l = \begin{cases}
sum_{l-1}+\sum_{i=l}^ {n-l+1}{a_i} & l\le n-l+1\\
sum_{l-1}-\sum_{i=n-l+1}^ {l}{a_i} & l> n-l+1\\
\end{cases}\)
\(sum\)可以用前缀和来维护且不用分类讨论(当\(l>n-l+1\)时前缀和会减出负数)
提面还要求对平均数\(\times n!\),即 $ \frac {sum_l}{l} \times n! $ 。可以使用前缀积(?),设从前向后的前缀积数组为\(q\),从后向前的前缀积数组为\(h\)每次\(sum_l=sum_l \times (q_l \times h_{l+1})\)即可。(避开区间长度不乘)
每个算法时间复杂度均为为\(O(n)\) 可过
\(\small {PS:这题需要模数,烦,放了个不模数的版本}\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#define int long long
using namespace std;
const int Max = 3e6+10;
int nnn=1;
int n,d,a[Max],q[Max],h[Max],ans1,ans2,ans3,sum[Max],Head,wid;
int maxn[Max],minn[Max];
signed main(){
//freopen("pst.in","r",stdin);
//freopen("pst.out","w",stdout);
scanf("%lld%lld",&n,&d);
for(register int i = 1;i <= n;++i){
scanf("%lld",&a[i]);
sum[i]=a[i]+sum[i-1];
}
for(int i = 1;i <= n;i++){
nnn=nnn*i;
}
if(d>=1){
for(register int i = 1;i <= n;++i){
ans1=ans1+a[i]*i*(n-i+1);
}
printf("%lld\n",ans1);
if(d>=2){
wid=0;
for(int i = 1;i <= n;i++){
while(Head&&a[i]>a[maxn[Head]]){
wid-=(maxn[Head]-maxn[Head-1])*a[maxn[Head]];
Head--;
}
wid+=(i-maxn[Head])*a[i];
maxn[++Head]=i;
ans2+=wid;
}
wid=0;
Head=0;
for(int i = 1;i <= n;i++){
while(Head&&a[i]<a[minn[Head]]){
wid-=(minn[Head]-minn[Head-1])*a[minn[Head]];
Head--;
}
wid+=(i-minn[Head])*a[i];
minn[++Head]=i;
ans2-=wid;
}
printf("%lld\n",ans2);
if(d>=3){
q[0]=h[n+1]=1;
for(int i=1;i<=n;i++){
q[i]=i*q[i-1];
h[n-i+1]=(n-i+1)*h[n-i+2];
}
int now=0,la=0;
for(register int i = 1;i <= n;++i){
now=(la+(sum[n-i+1]-sum[i-1]));
la=now;
now=now*q[i-1]*h[i+1];
ans3=ans3+now;
}
printf("%lld\n",ans3);
}
}
}
return 0;
}
\(\large{T2\ Present} \ \ \tiny{80 pst}\)
-
提意:对一个数列进行左端加入,右端加入,左端退出,右端退出,翻转。
-
没想到打标记,T了两个点
-
deque板子
- 优化:在翻转时可以声明一个变量 \(f\) 初始值为 0 ,当 \(f\) 标记为1时将接下来的所有操作反过来执行,每次翻转将 \(f\) 取反即可
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
const int Max = 5e5+10;
int n,id,md,x,f;
deque<int>q;
signed main(){
//freopen("prs.in","r",stdin);
//freopen("prs.out","w",stdout);
scanf("%lld%lld",&n,&id);
for(register int i = 1;i <= n;++i){
scanf("%lld",&md);
if(md==0){
scanf("%lld",&x);
if(f==1)q.push_front(x);
else q.push_back(x);
}
if(md==1){
if(!q.empty()){
if(f==1){
int t=q.front();
q.pop_front();
printf("%lld\n",t);
}
else{
int t=q.back();
q.pop_back();
printf("%lld\n",t);
}
}
else printf("0\n");
}
if(md==2){
scanf("%lld",&x);
if(f==1) q.push_back(x);
else q.push_front(x);
}
if(md==3){
if(!q.empty()){
if(f==1){
int t=q.back();
q.pop_back();
printf("%lld\n",t);
}
else{
int t=q.front();
q.pop_front();
printf("%lld\n",t);
}
}
else printf("0\n");
}
if(md==4){
if(f==0)f=1;
else f=0;
}
}
return 0;
}
\(\normalsize{T3\ Future} \ \ \tiny{80 pst}\)
-
提意:求一刻树的根到叶的最长路。
-
忘置负值了,T两个点
-
跑一遍最长路 SPFA 即可,丁真的树形dp似乎更优,但是我喜欢图论。
- 坑点:有复权,需要置 -inf !!!
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
const int Max = 1e5+10;
int n,w[Max],f[Max],d[Max],v[Max],t[Max],ans=-0x3f3f3f3ff3f3f3;
int Head[Max],Next[Max],Edge[Max],Ver[Max],tot;
queue<int>q;
inline void add(int x,int y,int z){
Edge[++tot]=z,Ver[tot]=y,Next[tot]=Head[x],Head[x]=tot;
}
inline void spfa(int s){
memset(d,0x80,sizeof d);
d[s]=w[s];v[s]=1;
q.push(s);
while(!q.empty()){
int x=q.front();q.pop();v[x]=0;
for(register int i = Head[x];i;i=Next[i]){
int y=Ver[i],z=Edge[i];
if(d[y]<d[x]+z){
d[y]=d[x]+z;
if(!v[y]){v[y]=1;q.push(y);}
}
}
}
}
signed main(){
freopen("ftr.in","r",stdin);
freopen("ftr.out","w",stdout);
scanf("%lld",&n);
for(register int i = 1;i <= n;i++){
scanf("%lld",&w[i]);
}
for(register int i = 1;i <= n;i++){
int x;
scanf("%lld",&x);
add(x,i,w[i]);
}
for(register int i = 1;i <= n;++i){
if(!Head[i])t[++t[0]]=i;
}
spfa(1);
for(register int i=1;i <=t[0];++i){
if(ans<d[t[i]]) ans=d[t[i]];
}
printf("%lld",ans);
return 0;
}
\(\normalsize{T4\ Byoned} \ \ \tiny{60 pst}\)
-
提意:给定一个 \(n \times n\) 的矩阵,每次可向左或向右走,每个点拥有一个值。\(past\):由\((1,1)\) 到对角线上点的路径中各点值的异或和。\(future\):由\((n,n)\)到对角线上点的路径中各点值的异或和。求使\(past+fate=future(fate为常数)\)的方案数
-
一眼以为是dp,结果发现不会推,果断选择爆搜,没想到正解真是爆搜(
-
以\((1,1)\)和\((n,n)\)为起点分别过一次dfs,用map存到达对角线所得的异或和的方案数。最后便利一遍map将两端的方案数相乘即可
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#define int long long
using namespace std;
const int Max = 30;
int n,fa,w[Max][Max],ans,cnt;
map<int,int>m,m2;
int dx[]={1,0};
int dy[]={0,1};
inline void dfs(int sum,int xx,int yy){
if(xx+yy-1>n) return;
if(xx+yy-1==n){
m[sum]++;
return;
}
for(register int i = 0;i < 2;i++){
int nx=dx[i]+xx,ny=dy[i]+yy;
if(nx>=1&&nx<=n&&ny>=1&&ny<=n){
dfs(sum^w[nx][ny],nx,ny);
}
}
}
inline void dfs2(int sum,int xx,int yy){
if(xx+yy-1<n) return;
if(xx+yy-1==n){
m2[sum]++;
return;
}
for(register int i = 0;i < 2;i++){
int nx=xx-dx[i],ny=yy-dy[i];
if(nx>=1&&nx<=n&&ny>=1&&ny<=n){
dfs2(sum^w[nx][ny],nx,ny);
}
}
}
signed main(){
//freopen("byd.in","r",stdin);
//freopen("byd.out","w",stdout);
scanf("%lld%lld",&n,&fa);
for(register int i = 1;i <= n;i++){
for(register int j = 1;j <= n;++j){
scanf("%lld",&w[i][j]);
}
}
dfs(0,1,1);
dfs2(0,n,n);
for(map<int,int>::iterator it = m.begin();it != m.end();++it){
if(m2.find(it->first+fa)!=m2.end())
{
ans+=(it->second)*(m2[it->first+fa]);
}
}
printf("%lld",ans);
return 0;
}
总接
大家谨慎卡场qwq
更新:快读查出来了
0:
inline int rea(){
int num=0,fl=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')fl=-1,c=getchar();
}
while(c>='0'&&c<='9'){
num=(num<<3)+(num<<1)+(c^48),c=getchar();
}
return fl*num;
}
1:
inline int rea(){
int num=0,fl=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')fl=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
num=(num<<3)+(num<<1)+(c^48),c=getchar();
}
return fl*num;
}
if(c=='-') fl=-1,c=getchar();
里错用了逗号导致输入卡住了(悲)
\(\Huge{我是傻逼}\)
Q:为什么博客这个B样
A:懒