8.16 普及模拟一

dengdengdong

噔 噔 咚

快读锅了 结果全部T掉QAQ (下面所说的得分是指赛后将快读改成scanf后的得分)


\(\large{T1\ Past} \ \ \tiny{40 pst}\)

  • 提意:求数列的所有子区间的和之和、极差之和、平均数之和。
  • 考场乱胡了个线段树就run了
  • 这题只能用\(O(n)\)的算法
  1. 子区间和之和:可以计算每个元素在每个子区间的出现次数,由打表可知:设数列长度为\(n\),元素在数列中的位置为\(i\)\(元素出现次数=i\times(n-i+1)\)
  2. 极差之和:用单调栈分别维护元素作为最大值的区间和元素作为最小值的区间。
    • 坑点:模之后可能会减出负数,需要加减运算时加模数。
  3. 平均数之和:由打表可知:设区间长度为\(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为常数)\)的方案数

    jb

  • 一眼以为是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:懒

posted @ 2023-08-17 16:40  tkt  阅读(30)  评论(5编辑  收藏  举报