2023.8.16 普及模拟1

众所周知,要有一张头图。

某些注意事项

image

这次比赛呢,就很无语……

\(total=50pts+80pts+80pts+\)"\(0\)"\(pts\)

题目感觉除了T1都好做,其它的题分数都错的很冤。就当教训吧。

T1 Past

题面

给出 \(n,d\) ,和序列 \(a\).

  • \(d=1\) 时,求所有的子区间和。
  • \(d=2\) 时,求所有子区间中最大的极差。
  • \(d=3\) 时,求所有子区间的平均数之和。

思路

部分分

前一半分还是好做的(考场也只得了这些分……)

只要暴力 \(O(n^3)\) 的算法就行,当然如果使用 ST算法 或 其他优化算法 可得 \(60\) 分或更高。

\(100pts\)

image

还有一些需要注意的细节,如要把负数转正,模数时要注意

赛时代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e6+10,mod=1336363663;
int maxx,minn,n,d,a[N],sum[N],b[N],ans,jc,tot=1;
double pj;
inline int read(){
    int x=0;bool f=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
    return f?-x:x;
}
signed main(void){
    freopen("pst.in","r",stdin);
    freopen("pst.out","w",stdout);
    n=read();d=read();
    for(int i=1;i<=n;++i){
        a[i]=read();
        sum[i]=(sum[i-1]+a[i])%mod;
    }
    if(!d) return 0;
    if(d==1){
        for(int i=1;i<=n;++i){
            for(int l=1;l+i-1<=n;++l){
                int r=l+i-1;
                ans=(ans+sum[r]-sum[l-1])%mod;
            }
        }
        printf("%lld",ans);
    }
    if(d==2){
        for(int i=1;i<=n;++i){
            for(int l=1;l+i-1<=n;++l){
                int r=l+i-1;
                maxx=-0x3f3f3f3f,minn=0x3f3f3f3f;
                ans=(ans+sum[r]-sum[l-1])%mod;
                for(int k=l;k<=r;++k){
                    maxx=maxx>a[k]?maxx:a[k];
                    minn=minn<a[k]?minn:a[k];
                }
                jc=(jc+maxx-minn)%mod;
            }
        }
        printf("%lld\n%lld",ans,jc);
    }
    if(d==3){
        for(int i=1;i<=n;++i){
            tot=tot*i%mod;
            for(int l=1;l+i-1<=n;++l){
                int r=l+i-1;
                maxx=-0x3f3f3f3f,minn=0x3f3f3f3f;
                ans=(ans+sum[r]-sum[l-1])%mod;
                for(int k=l;k<=r;++k){
                    maxx=maxx>a[k]?maxx:a[k];
                    minn=minn<a[k]?minn:a[k];
                }
                jc=(jc+maxx-minn)%mod;
                pj=pj+(double)(1.0*(sum[r]-sum[l-1])/i);
            }
        }
        printf("%lld\n%lld\n",ans,jc);
        cout<<(int)(pj*1.0*tot)%mod;
    }
    return 0;
/*
3 3
3 2 5
*/
}

赛后代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e6+10,mod=1336363663;
int maxx,minn,n,d,a[N],sum[N],b[N],ans,jc,tot,pp;
int o[N],f1[N],f2[N];
double pj;stack<int>q,p;
inline int read(){
    int x=0;bool f=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
    return f?-x:x;
}
signed main(void){
    freopen("pst.in","r",stdin);
    freopen("pst.out","w",stdout);
    n=read();d=read();
    f1[0]=f2[n+1]=1;
    for(int i=1;i<=n;++i){
        a[i]=read();
        sum[i]=(sum[i-1]+a[i])%mod;
        f1[i]=f1[i-1]*i%mod;
    }
    for(int i=n;i;--i){
    	f2[i]=f2[i+1]*i%mod;
	}
    if(!d) return 0;
    if(d==1){
        for(int i=1;i<=n;++i) 
			ans=(ans+a[i]*i%mod*(n-i+1))%mod;
        //它前面的和后面的 
        printf("%lld",ans);
    }
    if(d==2){
        for(int i=1;i<=n;++i) ans=(ans+a[i]*i%mod*(n-i+1)%mod)%mod;
        a[n+1]=LLONG_MAX;
        for(int x,i=1;i<=n+1;++i){
        	while(!q.empty()&&a[q.top()]<=a[i]){
        		x=q.top();q.pop(); 
        		jc=(jc+a[x]*((i-o[x]-1)%mod+(int)(x-o[x]-1)*(i-x-1)%mod+mod)%mod)%mod;	
			}
			if(q.empty()) o[i]=0;
			else o[i]=q.top();
			q.push(i);
		}
		stack<int>().swap(q);
		a[n+1]=-114154;
		for(int x,i=1;i<=n+1;++i){
        	while(!q.empty()&&a[q.top()]>=a[i]){
        		x=q.top();q.pop();
        		jc=(jc-a[x]*((i-o[x]-1)%mod+1ll*(x-o[x]-1)*(i-x-1)%mod+mod)%mod+mod)%mod;	
			}
			if(q.empty()) o[i]=0;
			else o[i]=q.top();
			q.push(i);
		}
		printf("%lld\n%lld",ans,jc);
    }
    if(d==3){
    	for(int i=1;i<=n;++i) ans=(ans+a[i]*i%mod*(n-i+1)%mod)%mod;
        a[n+1]=LLONG_MAX;
        for(int x,i=1;i<=n+1;++i){
        	while(!q.empty()&&a[q.top()]<=a[i]){
        		x=q.top();q.pop(); 
        		jc=(jc+a[x]*((i-o[x]-1)%mod+(int)(x-o[x]-1)*(i-x-1)%mod+mod)%mod)%mod;	
			}
			if(q.empty()) o[i]=0;
			else o[i]=q.top();
			q.push(i);
		}
		stack<int>().swap(q);
		a[n+1]=-114154;
		for(int x,i=1;i<=n+1;++i){
        	while(!q.empty()&&a[q.top()]>=a[i]){
        		x=q.top();q.pop();
        		jc=(jc-a[x]*((i-o[x]-1)%mod+1ll*(x-o[x]-1)*(i-x-1)%mod+mod)%mod+mod)%mod;	
			}
			if(q.empty()) o[i]=0;
			else o[i]=q.top();
			q.push(i);
		}
    	for(int i=0;i<n;++i){
    		pp=(pp+sum[n-i]-sum[i]+mod)%mod;
    		tot=(tot+pp*f1[i]%mod*f2[i+2])%mod;
		}
		printf("%lld\n%lld\n%lld",ans,jc,tot);
	}
    return 0;
/*
3 3
3 2 5
*/
}

T2 Present

题面

模拟双端队列题,\(5\) 种操作。

  • 往前出入一个数
  • 查询前面第一个数
  • 往后出入一个数
  • 查询后面第一个数
  • 翻转数列

思路

水题,唯一需要注意的是不要暴力翻转数列,可以拿一个变量记录是否反转,在做。(没想到这一点,悲)

赛时代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
deque<int>q;
int n,id,a[N];
signed main(void){
    freopen("prs.in","r",stdin);
    freopen("prs.out","w",stdout);
    scanf("%d%d",&n,&id);
    for(int opt,x,o,i=1;i<=n;++i){
        scanf("%d",&opt);
        if(opt==0){
            scanf("%d",&x);
            q.push_front(x);
        }
        if(opt==1){
            if(!q.size()){
                cout<<0<<endl;
            }
            else{
                cout<<q.front()<<endl;
                q.pop_front();
            }
        }
        if(opt==2){
            scanf("%d",&x);
            q.push_back(x);
        }
        if(opt==3){
            if(!q.size()){
                cout<<0<<endl;
            }
            else{
                cout<<q.back()<<endl;
                q.pop_back();
            }
        }
        if(opt==4){
            o=q.size();
            for(int i=1;i<=o;++i){
                a[i]=q.front();
                q.pop_front();
            }
            for(int i=o;i;--i){
                q.push_back(a[i]);
                a[i]=0;
            }
        }
    }
    return 0;
/*
8 0
1
2 111
0 222
4
0 333
3
1
3
*/
}

赛后代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
deque<int>q;bool pd; 
int n,id,a[N];
signed main(void){
    freopen("prs.in","r",stdin);
    freopen("prs.out","w",stdout);
    scanf("%d%d",&n,&id);
    for(int opt,x,o,i=1;i<=n;++i){
        scanf("%d",&opt);
        if(opt==0){
            scanf("%d",&x);
            if(!pd) q.push_front(x);
            else q.push_back(x);
        }
        if(opt==1){
            if(!q.size()) puts("0");
            else{
            	if(!pd){
            		cout<<q.front()<<endl;
                	q.pop_front();
				}
               else{
            		cout<<q.back()<<endl;
                	q.pop_back();
			   }
            }
        }
        if(opt==2){
            scanf("%d",&x);
            if(!pd) q.push_back(x);
            if(pd) q.push_front(x);
        }
        if(opt==3){
            if(!q.size()) puts("0");
            else{
            	if(!pd){
            		cout<<q.back()<<endl;
                	q.pop_back();
				}
                else{
                	cout<<q.front()<<endl;
                	q.pop_front();
				}
            }
        }
        if(opt==4){
            if(pd) pd=0;
            else pd=1;
        }
    }
    return 0;
/*
8 0
1
2 111
0 222
4
0 333
3
1
3
*/
}

T3 Future

题面

一棵树,问从根节点(编号为 \(1\) 的节点)到叶子结点的最大距离(叶子结点任意,不过要是最长距离),边都是有向的。

思路

法一:SPFA 跑最长路

只需注意两点,把点权变成边权,初始值负无穷(没注意到为 \(90\) 分)。赛时刚开始是这么写的,但又想这毕竟是死掉的算法,就换成了树形dp。

code

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
int ans=-0x3f3f3f3f,n,h[N],cnt,s[N],d[N],a[N];
struct node{int to,nxt,w;}e[N];
inline void add(int x,int y,int z){
    e[++cnt].to=y;e[cnt].nxt=h[x];e[cnt].w=z;
    h[x]=cnt;
}
queue<int>q;bool vis[N];
inline void spfa(){
    memset(a,0x3f,sizeof a);
    memset(vis,0,sizeof vis);
	q.push(1);a[1]=s[1];vis[1]=1;
	while(!q.empty()){
		int u=q.front();q.pop();vis[u]=false;
		for(int i=h[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(a[v]>a[u]+e[i].w){
				a[v]=a[u]+e[i].w;
				if(!vis[v]){
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
    return ;
}
signed main(void){
    freopen("ftr.in","r",stdin);
    freopen("ftr.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;++i) scanf("%lld",s+i);
    for(int x,i=1;i<=n;++i){
        scanf("%lld",&x);
        add(x,i,s[i]);
        ++d[x];
    }
    spfa();
    for(int i=1;i<=n;++i){
        if(!d[i]){
            ans=max(ans,a[i]);
        }
    }
    printf("%lld\n",ans);
/*
5
1 2 3 4 5
0 1 2 3 2
*/
}

法二:树形dp

板子,不多说,注意初始值,和判叶子结点。

code

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
int ans,n,h[N],cnt,s[N],d[N],a[N],dp[N];
struct node{int to,nxt,w;}e[N];
inline void add(int x,int y,int z){
    e[++cnt].to=y;e[cnt].nxt=h[x];e[cnt].w=z;
    h[x]=cnt;
}
// queue<int>q;bool vis[N];
// inline void spfa(){
//     memset(a,0x3f,sizeof a);
//     memset(vis,0,sizeof vis);
// 	q.push(1);a[1]=s[1];vis[1]=1;
// 	while(!q.empty()){
// 		int u=q.front();q.pop();vis[u]=false;
// 		for(int i=h[u];i;i=e[i].nxt){
// 			int v=e[i].to;
// 			if(a[v]>a[u]+e[i].w){
// 				a[v]=a[u]+e[i].w;
// 				if(!vis[v]){
// 					q.push(v);
// 					vis[v]=1;
// 				}
// 			}
// 		}
// 	}
//     return ;
// }
inline void dfs(int x,int fa){
    if(!h[x]) dp[x]=0;
    for(int i=h[x];i;i=e[i].nxt){
        int v=e[i].to;
        dfs(v,x);
        dp[x]=max(dp[x],dp[v]);
    }
    dp[x]=dp[x]+s[x];
    // cout<<x<<" "<<dp[x]<<endl;
}
signed main(void){
    freopen("ftr.in","r",stdin);
    freopen("ftr.out","w",stdout);
    memset(dp,-0x3f,sizeof dp);
    scanf("%lld",&n);
    for(int i=1;i<=n;++i) scanf("%lld",s+i);
    for(int x,i=1;i<=n;++i){
        scanf("%lld",&x);
        add(x,i,s[i]);
        ++d[x];
    }
    // spfa();
    // for(int i=1;i<=n;++i){
    //     if(!d[i]){
    //         ans=max(ans,a[i]);
    //     }
    // }
    dfs(1,0);
    printf("%lld\n",dp[1]);
/*
5
1 2 3 4 5
0 1 2 3 2
*/
}

法3:拓扑优化dp,弱化版缩点

奇奇怪怪的做法捏……

T4 Beyond

题面太长,就不放了。

思路

meet in middle ,分别从 \((1,1)\) (往 对角线上的点 搜索) 和 对角线上的点(往 \((n,n)\) 搜索) 进行爆搜,当到达当下区域时,将所得到的值分别用两个 map 进行存储,又因为在当下区域可以进行传送至另一个当下区域,然后利用加法原理和乘法原理求解。
注意:当遍历 map 时用了range-based for + auto,请注意使用 C++ 14 -O2 ,不然会 CE(血的教训)。

(range-based for+auto)+(C++ -O2)+(\(AC\) 代码) = "\(0\)" 分

赛时( \(AC\) )代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100;
const int dx[]={1,0};
const int dy[]={0,1};
int ans,n,F,a[N][N],dp[N][N];
map<int,int>p,q;
bool vis[N][N];
inline int read(){
    int x=0;bool f=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
    return f?-x:x;
}
inline void dss(int x,int y,int cnt){
    if(x+y-1==n){
        ++q[cnt];
        return ;
    }
    for(int i=0;i<2;++i){
        int xx=x+dx[i],yy=y+dy[i];
        if(xx<=n&&y<=n&&x+y-1<=n)
            dss(xx,yy,cnt^a[xx][yy]);
    }
}
inline void dff(int x,int y,int cnt){
    if(x==n&&y==n){
        ++p[cnt];
        return ;
    }
    for(int i=0;i<2;++i){
        int xx=x+dx[i],yy=y+dy[i];
        if(xx<=n&&y<=n)
            dff(xx,yy,cnt^a[xx][yy]);
    }
}
signed main(void){
    freopen("byd.in","r",stdin);
    freopen("byd.out","w",stdout);
    n=read();F=read();
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            a[i][j]=read();
        }
    }
    // if(n==1){
    //     if(a[1][1]+F==a[1][1]) ++ans;
    //     printf("%lld\n",ans);
    // }
    // else if(n==2){
    //     int x=a[1][1]^a[1][2],y=a[1][1]^a[2][1];
    //     int z=x^a[2][2],g=y^a[2][2];
    //     if(x+F==z) ans+=2;
    //     if(y+F==g) ans+=2;
    //     printf("%lld\n",ans);
    // }
    // else{
    dss(1,1,0);
    for(int i=1;i<=n;++i) dff(i,n-i+1,0);
    for(auto i:p){
        int o=i.first-F;
        if(q.find(o)!=q.end()) ans+=i.second*q[o];
    }
    printf("%lld\n",ans);
    // }
    return 0;
}

总结

选好编译环境,看清呐,我还以为学校OJ的 C++ -O2 默认 C++14 呢,还要注意文件输入输出。
image
image

posted @ 2023-08-16 14:11  GOD_HJ  阅读(55)  评论(2编辑  收藏  举报