20211111 NOIP模拟赛
前言
好累呀!累死了!好不容易改完三道题!蒟蒻咆哮!!
日常挂分.jpg(数组开大爆了)
垫底次数+=1;
正题
T1:(简 单 图 论)
得分:
刚了一个小时放弃,然后乱写一个搜索。
正解:
将边按照d值的大小排序,从小到大依次枚举解锁当前边的时间情况。
使用三个矩阵:
其中a,b可以用bitset代替。f每一次用Floyd更新。
当枚举到一条边时,距离解锁该边还要走步,我们就考虑在这几步中,他可以到达那些节点。以此更新b。
每走一步,a就要乘上自己一次,更新路径数量,再把a的幂与b相乘,这样更新b矩阵,注意这里a不会变,我们只使用它的值来乘方,乘方后的矩阵就是所有的走的情况。
然后使用Floyd更新新加入的边对于路径长度的影响。
最后记录当前的,再更新a。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m;
struct node{
int u,v,d;
bool operator < (const node &b){
return d<b.d;
}
}p[160];
const int N=160;
bitset<N> a[N],b[N];
int f[N][N];
void Mul(bitset<N> *a,bitset<N> *b){
bitset<N> ret[N];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]){
ret[i]|=b[j];
}
}
}
for(int i=1;i<=n;i++){
a[i]=ret[i];
}
return ;
}
void Pow(bitset<N> *a,int b){
bitset<N> ret[N];
for(int i=1;i<=n;i++){
ret[i][i]=1;
}
while(b!=0){
if(b&1){
Mul(ret,a);
}
Mul(a,a);
b>>=1;
}
for(int i=1;i<=n;i++){
a[i]=ret[i];
}
return ;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>p[i].u>>p[i].v>>p[i].d;
}
sort(p+1,p+m+1);
memset(f,inf,sizeof f);
for(int i=1;i<=n;i++){//路径长度
f[i][i]=0;
}
int cnt=0;
int ans=inf;
for(int i=1;i<=n;i++){
b[i][i]=1;//自己可以到达自己
}
for(int i=1;i<=m;i++){
int x=p[i].u,y=p[i].v,d=p[i].d;
int delta=d-cnt;//$\Delta d$
bitset<N> tmp[N];
for(int i=1;i<=n;i++){
tmp[i]=a[i];
}
Pow(tmp,delta);//快速幂
Mul(b,tmp);
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
f[j][k]=min(f[j][k],f[j][x]+1+f[y][k]);//更新路径长度
}
}
for(int j=1;j<n;j++){
if(b[1][j]){
ans=min(ans,d+f[j][n]);//更新答案
}
}
a[x][y]=1;//更新a
cnt=d;
}
cout<<ans<<endl;
return 0;
}
T2:
注意空间,注意空间,注意空间!!!
一道我最爱的构造题,可惜挂成0分了!
思路:
先不管其他限制,把矩阵按照顺序排列好。
这时,如果是奇数就不管。
如果是偶数,每一行的数值是,每一列的数值是
不管是奇数还是偶数,每一列的数值是不变的整数,而每一行的数值在为偶数时不成立。
因为我们要把每一行的数值改为整数,所以每一行的总和要加或减的值。
可以把奇数行的最后一个与偶数行的个交换,这样两行的数值都变为了整数。
当时,这两列交换了偶数次,两列的数值为整数,但当时,两行交换了奇数次,不满足要求。
为了让这两列满足要求,我们在最后两行交换时,交换这样矛盾就转移到了第列和第列上,为了消除,交换这两列其他某一行的两个元素即可,这样两列同时加上,减去n,数值还是整数。
点击查看代码
#include<bits/stdc++.h>
#define re register
//Orz huaruoji Orz llmmkk Orz _Iva Orz pigonered
using namespace std;
int sit[5][5]={{0,0,0,0,0},{0,1,2,3,6},{0,4,5,7,8},{0,10,9,11,14},{0,13,12,15,16}};
int a[2001][2001];
int n;
int main(){
// freopen("s.in","r",stdin);
// freopen("s.out","w",stdout);
scanf("%d",&n);
if(n==2||n==0){
cout<<-1<<endl;
return 0;
}else if(n%2==1){
for(re int i=1;i<=n;i++){
for(re int j=1;j<=n;j++){
cout<<(i-1)*n+j<<' ';
}
cout<<"\n";
}
return 0;
}else if(n%4==0){
for(re int i=1;i<=4;i++){
for(re int j=1;j<=4;j++){
a[i][j]=sit[(i-1)%4+1][(j-1)%4+1];
}
}
for(re int i=1;i<=4;i++){
for(re int j=5;j<=n;j++){
a[i][j]=a[i][j-4]+16;
}
}
for(int i=5;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=a[i-4][j]+16*(n/4);
}
}
for(re int i=1;i<=n;i++){
for(re int j=1;j<=n;j++){
cout<<a[i][j]<<' ';
}
cout<<"\n";
}
//cout<<check()<<endl;
}else if(n%2==0){//正解
int cnt =1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=cnt++;
}
}
for(int i=1;i<=n-3;i+=2){
swap(a[i][n],a[i+1][n/2]);
}
swap(a[n-1][n-1],a[n][n/2-1]);
swap(a[1][n-1],a[1][n/2-1]);
for(re int i=1;i<=n;i++){
for(re int j=1;j<=n;j++){
cout<<a[i][j]<<' ';
}
cout<<"\n";
}
//cout<<check()<<endl;
}
return 0;
}
T3:
暴力可以有50分,可惜没时间了。
问题等价于从一个矩阵的向上和右上走,每走一步会有的贡献,最小化贡献。
tj原文:
发现一个神奇的性质,就是最优策略一定是从走到某一列然后一直走到底因为如果
走到那一列更优那么一定会以最快的速度走到那一列,不可能出现转两次弯的情况,只能转一次弯。
证明:
如果转两次弯:
证明完成!
思路:
对于每一个询问,考虑在y前面的x个数中选出x个数的最小和,因为每个数是又更新而来的,要想最优就选一列走到底。
我们考虑最优的情况,第一个数选i个,后面的x-i个数就每个选1个,这样是最优的情况。
暴力是枚举每一列,然后算ans。有50分。
正解是使用线段树维护单调栈,找到单调递增的一个序列,在枚举序列上的每个点。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define in read()
using namespace std;
inline int read(){
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-'){
sign=-1;
}
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
const int N=1e6+100;
int n,Q;
int a[N],len,q[N],sum[N],lst[N];
int bin(int k){
int l=1,r=len,mid;
while(l<r){
mid=l+r>>1;
if(q[mid]<k){
l=mid+1;
}else{
r=mid;
}
}
return l;
}
int maxx[N*4];
void update(int k,int l,int r,int x,int v){
if(l==r){
maxx[k]=max(v,maxx[k]);
return ;
}
int mid=l+r>>1;
if(x<=mid){
update(k<<1,l,mid,x,v);
}else{
update(k<<1|1,mid+1,r,x,v);
}
maxx[k]=max(maxx[k<<1],maxx[k<<1|1]);
}
int query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y){
return maxx[k];
}
int res=0;
int mid=l+r>>1;
if(x<=mid){
res=max(res,query(k<<1,l,mid,x,y));
}
if(mid<y){
res=max(res,query(k<<1|1,mid+1,r,x,y));
}
return res;
}
signed main(){
// freopen("y2.in","r",stdin);
// freopen("y.out","w",stdout);
n=in,Q=in;
for(int i=1;i<=n;i++){
a[i]=in,q[i]=a[i];
sum[i]=sum[i-1]+a[i];
}
sort(q+1,q+n+1);
len=unique(q+1,q+n+1)-(q+1);
for(int i=1;i<=n;i++){
int qr=bin(a[i])-1;
if(qr){
lst[i]=query(1,1,n,1,qr);//单调栈的前一项
}else{
lst[i]=0;
}
update(1,1,n,qr+1,i);
}
while(Q--){
int x=in,y=in;
int ans=1e18;
for(int i=y;i>0&&i>y-x;i=lst[i]){
ans=min(ans,sum[y]-sum[i]+(x-y+i)*a[i]);//贡献
}
cout<<ans<<endl;
}
return 0;
}
T4 咕咕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】