8.10考试总结(NOIP模拟35)[玩游戏·排列·最短路·矩形]
所谓人,无论是谁到了最后,都会形单影只。
T1 玩游戏
解题思路
可以把序列从 k 位置掰成两个序列。
问题就变成了两个序列从开头走向末尾是否可以保证前缀和之和一直不大于 0 。
并且可以移动到两个序列的末尾,问题就变成处理前缀和。
然后在每一个序列里维护一个 next 值,表示可以跳到的较小值。
这里需要正反扫一遍,毕竟只扫一边的话会有最小值一边的 next 无法更新。
然后就是对于两个序列分别从两边扫一边。
看看是否可以跳到对应的 next 位置。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10,INF=1e18;
int T,n,k,l,r,sum,s[N],nxt1[N],nxt2[N];
int a[N],b[N],cnt1,cnt2,pre1[N],pre2[N];
void solve()
{
memset(nxt1,0,sizeof(nxt1));
memset(nxt2,0,sizeof(nxt2));
n=read(); k=read();
cnt1=cnt2=0;
for(int i=1;i<=n;i++)
s[i]=read();
a[++cnt1]=0; b[++cnt2]=0;
for(int i=k+1;i<=n;i++)
a[++cnt1]=s[i];
for(int i=k;i>1;i--)
b[++cnt2]=s[i];
for(int i=1;i<=cnt1;i++)
pre1[i]=pre1[i-1]+a[i];
for(int i=1;i<=cnt2;i++)
pre2[i]=pre2[i-1]+b[i];
int pos,minn;
minn=pre1[1]; pos=1;
for(int i=2;i<=cnt1;i++)
if(minn>=pre1[i])
{
nxt1[pos]=i;
pos=i;
minn=pre1[i];
}
minn=pre1[cnt1]; pos=cnt1;
for(int i=cnt1-1;i>=1;i--)
if(minn>pre1[i])
{
nxt1[pos]=i;
pos=i;
minn=pre1[i];
}
minn=pre2[1]; pos=1;
for(int i=2;i<=cnt2;i++)
if(minn>=pre2[i])
{
nxt2[pos]=i;
pos=i;
minn=pre2[i];
}
minn=pre2[cnt2]; pos=cnt2;
for(int i=cnt2-1;i>=1;i--)
if(minn>pre2[i])
{
nxt2[pos]=i;
pos=i;
minn=pre2[i];
}
if(pre1[cnt1]+pre2[cnt2]>0)
{
printf("No\n");
return ;
}
int pos1=1,pos2=1;
bool flag=true;
while(nxt1[pos1]||nxt2[pos2])
{
flag=true;
for(int i=pos1+1;i<=nxt1[pos1];i++)
if(pre1[i]+pre2[pos2]>0)
{
flag=false;
break;
}
if(!nxt1[pos1]) flag=false;
if(flag){pos1=nxt1[pos1];continue;}
flag=true;
for(int i=pos2+1;i<=nxt2[pos2];i++)
if(pre1[pos1]+pre2[i]>0)
{
flag=false;
break;
}
if(!nxt2[pos2]) flag=false;
if(!flag) break;
pos2=nxt2[pos2];
flag=true;
}
if(!flag)
{
printf("No\n");
return ;
}
pos1=cnt1;pos2=cnt2;
while(nxt1[pos1]||nxt2[pos2])
{
flag=true;
for(int i=pos1-1;i>=nxt1[pos1];i--)
if(pre1[i]+pre2[pos2]>0)
{
flag=false;
break;
}
if(!nxt1[pos1]) flag=false;
if(flag){pos1=nxt1[pos1];continue;}
flag=true;
for(int i=pos2-1;i>=nxt2[pos2];i--)
if(pre1[pos1]+pre2[i]>0)
{
flag=false;
break;
}
if(!nxt2[pos2]) flag=false;
if(!flag) break;
pos2=nxt2[pos2];
flag=true;
}
if(!flag)
{
printf("No\n");
return ;
}
printf("Yes\n");
}
signed main()
{
T=read();
while(T--) solve();
return 0;
}
排列
解题思路
动态规划。
DP 数组 \(f_{i,j,0/1,0/1}\) 表示区间长度为 i 操作 j 次正好可以只剩下一个的方案数。
0 或者 1 分别表示左边或者右边是否有更大的或者边界。
然后就可以通过之前的状态和组合数进行转移。
分别枚举现在区间的长度,操作次数,子区间长度,以及子区间操作次数进行转移。
转移的时候就好像在两个子区间中间插进去一个更大的数,其实是和挨着边界差不多的。
但是这样显然会 TLE 因此需要前缀和优化。
转移也是差不多的,只不过省掉了一维枚举子区间的操作。
然后直接整阶乘进行组合数的计算是会被卡常的,因此需要杨辉三角处理。。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e3+10;
int n,m,mod,c[N][N],f[N][15][2][2];
void init()
{
c[0][0]=1;
for(int i=1;i<=n;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
signed main()
{
n=read(); m=read(); mod=read();
init();
for(int i=0;i<=m;i++)
f[0][i][0][0]=f[0][i][1][0]=f[0][i][0][1]=f[0][i][1][1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=i;k++)
{
f[i][j][0][0]=(f[i][j][0][0]+f[k-1][j][0][1]*f[i-k][j][1][0]%mod*c[i-1][k-1])%mod;
f[i][j][1][0]=(f[i][j][1][0]+f[k-1][j][1][0]*f[i-k][j-1][1][1]%mod*c[i-1][k-1])%mod;
f[i][j][0][1]=(f[i][j][0][1]+f[k-1][j][0][1]*f[i-k][j-1][1][1]%mod*c[i-1][k-1])%mod;
int tmp1=(f[k-1][j][1][1]-f[k-1][j-1][1][1]+mod)%mod;
int tmp2=(f[i-k][j][1][1]-f[i-k][j-1][1][1]+mod)%mod;
f[i][j][1][1]=(f[i][j][1][1]+(f[k-1][j][1][1]*f[i-k][j][1][1]%mod-tmp1*tmp2%mod+mod)*c[i-1][k-1])%mod;
}
printf("%lld",(f[n][m][0][0]-f[n][m-1][0][0]+mod)%mod);
return 0;
}
最短路
解题思路
考场上是想了一个假做法,先从 1 为源点跑一边,用 bitset 进行维护。
然后再第一次的 Dij 的基础上继承以后再跑一次,更新答案。
然后这个做法是 WA 了两个点,但是由于测试是 subtask 的,所以就 20pts 了。
Yubai 的思路非常的妙!!
对于每一个有的边建一条反边,跑 二维DIJ。
\(dis_{i,j}\) 表示 从 1 节点沿正边到达 i 点以及沿反边到 j 点的距离之和。
这样 \(dis_{n,n}\) 表示的就是从 1 沿正边到 n 在沿正边走回来的距离。
同样用 bitset 维护,因为这个是动态的,因此具有正确性。
二维Dij 的时候注意两步不可以同时进行要一步一步来。。
通俗来讲就是两个循环而不是两层循环。。。
code
20ptsWA两个点
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=260,M=N*N,INF=1e18;
int n,m,ans=INF,s[N],dis[N],dis2[N];
int tot=1,head[N],nxt[M],ver[M],edge[M];
bitset<N> bit[N],bit2[N];
bool vis[N],b[M];
priority_queue<pair<int,int> > q;
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void check(int x)
{
b[x]=true;
for(int i=head[x];i;i=nxt[i])
if(!b[ver[i]]&&vis[ver[i]])
check(ver[i]);
}
bool judge()
{
bool jud1,jud2;
check(1); jud1=b[n];
memset(b,false,sizeof(b));
check(n); jud2=b[1];
memset(b,false,sizeof(b));
return jud1&&jud2;
}
void Dij()
{
memset(dis,0x3f,sizeof(dis));
dis[1]=s[1]; bit[1][1]=true;
q.push(make_pair(-dis[1],1));
while(!q.empty())
{
int x=q.top().second;q.pop();
if(vis[x]) continue;
vis[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i],sum=0;
bitset<N> bi=bit[to]|bit[x];
for(int j=1;j<=n;j++)
if(bi[j])
sum+=s[j];
if(!bi[to])
{
bi[to]=true;
sum+=s[to];
}
if(sum<dis[to])
{
dis[to]=sum;
bit[to]=bi;
q.push(make_pair(-dis[to],to));
}
}
}
}
void Dij2()
{
memset(dis2,0x3f,sizeof(dis2));
dis2[n]=dis[n]; bit2[n]=bit[n];
q.push(make_pair(-dis2[n],n));
while(!q.empty())
{
int x=q.top().second;q.pop();
if(vis[x]) continue;
vis[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i],sum=0;
bitset<N> bi=bit2[to]|bit2[x];
for(int j=1;j<=n;j++)
if(bi[j])
sum+=s[j];
if(!bi[to])
{
bi[to]=true;
sum+=s[to];
}
if(sum<dis2[to])
{
dis2[to]=sum;
bit2[to]=bi;
q.push(make_pair(-dis2[to],to));
}
}
}
}
void dfs(int x,int cnt)
{
if(x==n)
{
if(judge()) ans=min(ans,cnt);
return ;
}
dfs(x+1,cnt);
vis[x]=true;
dfs(x+1,cnt+s[x]);
vis[x]=false;
}
void solve()
{
memset(vis,false,sizeof(vis));
vis[1]=vis[n]=true;
dfs(2,s[1]+s[n]);
printf("%lld",ans);
exit(0);
}
signed main()
{
n=read(); m=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<=m;i++)
{
x=read(); y=read();
add_edge(x,y);
}
memset(vis,true,sizeof(vis));
if(!judge())
{
printf("-1");
return 0;
}
memset(vis,false,sizeof(vis));
Dij();
memset(vis,false,sizeof(vis));
Dij2();
printf("%lld",dis2[1]);
return 0;
}
正解
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=260,M=N*N,INF=0x3f3f3f3f3f3f3f3f;
int n,m,s[N],dis[N][N];
int tot,head[N],nxt[M],ver[M];
int tot2,head2[N],nxt2[M],ver2[M];
bool vis[N][N];
bitset<N> bit[N][N];
struct Node
{
int dat,x,y;
bool friend operator < (Node x,Node y)
{
return x.dat>y.dat;
}
};
priority_queue<Node> q;
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void add_edge2(int x,int y)
{
ver2[++tot2]=y;
nxt2[tot2]=head2[x];
head2[x]=tot2;
}
void Dij()
{
memset(dis,0x3f,sizeof(dis));
dis[1][1]=s[1];
bit[1][1][1]=true;
q.push((Node){dis[1][1],1,1});
while(!q.empty())
{
int x=q.top().x,y=q.top().y;q.pop();
if(vis[x][y]) continue;
vis[x][y]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i],temp=0;
if(!bit[x][y][to]) temp=s[to];
if(dis[x][y]+temp<dis[to][y])
{
bit[to][y]=bit[x][y];
bit[to][y][to]=true;
dis[to][y]=dis[x][y]+temp;
q.push((Node){dis[to][y],to,y});
}
}
for(int i=head2[y];i;i=nxt2[i])
{
int to=ver2[i],temp=0;
if(!bit[x][y][to]) temp=s[to];
if(dis[x][y]+temp<dis[x][to])
{
bit[x][to]=bit[x][y];
bit[x][to][to]=true;
dis[x][to]=dis[x][y]+temp;
q.push((Node){dis[x][to],x,to});
}
}
}
}
signed main()
{
n=read(); m=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<=m;i++)
{
x=read(); y=read();
add_edge(x,y);
add_edge2(y,x);
}
Dij();
printf("%lld",dis[n][n]==INF?-1ll:dis[n][n]);
return 0;
}
T4 矩形
解题思路
(upd in 8.14)
线段树+扫描线。
先按照 x 坐标排序,然后线段树建立在 y 轴上。
用于记录当前区间内有的矩形的个数,消失最晚(也就是右边界最大的)的矩形的右边界以及标号。
然后种类只记为 1 或者 2,分别表示整个区间内只有一种或者两个子区间内不同(主要用于继续向下查询。)
接下来从左往右扫一边,先查询左边界是否有合法的矩形,在用右边界更新就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10;
struct Segment_Tree
{
int id,spe,lim;
bool laz;
Segment_Tree(){spe=1;}
}tre[N<<2];
struct Node
{
int x,y,x2,y2;
bool friend operator < (Node a,Node b)
{
return a.x<b.x;
}
}s[N];
int n,mx,ans,fa[N];
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void push_up(int x)
{
if(tre[ls].id==tre[rs].id)
{
tre[x].id=tre[ls].id;
tre[x].lim=tre[ls].lim;
tre[x].spe=1;
}
else tre[x].spe=2;
if(tre[ls].spe==2||tre[rs].spe==2) tre[x].spe=2;
}
void push_down(int x)
{
if(!tre[x].laz) return;
tre[ls].laz=tre[rs].laz=true;
tre[ls].id=tre[rs].id=tre[x].id;
tre[ls].lim=tre[rs].lim=tre[x].lim;
tre[x].laz=false;
}
void update(int x,int l,int r,int L,int R,int lim,int id)
{
if(L<=l&&r<=R&&tre[x].spe==1)
{
if(tre[x].lim>lim) return ;
tre[x].laz=true;
tre[x].id=id;
tre[x].lim=lim;
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,lim,id);
if(R>mid) update(rs,mid+1,r,L,R,lim,id);
push_up(x);
}
void work(int x,int l,int r,int L,int R,int lim,int id)
{
if(L<=l&&r<=R&&tre[x].spe==1)
{
if(tre[x].lim>=lim)
fa[find(id)]=find(tre[x].id);
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid) work(ls,l,mid,L,R,lim,id);
if(R>mid) work(rs,mid+1,r,L,R,lim,id);
push_up(x);
}
signed main()
{
n=read();
for(int i=1,x,y,x2,y2;i<=n;i++)
{
fa[i]=i;
x=read(); y=read(); x2=read(); y2=read();
s[i]=(Node){x,y,x2,y2};
mx=max(mx,y2);
}
sort(s+1,s+n+1);
for(int i=1;i<=n;i++)
{
work(1,1,mx,s[i].y,s[i].y2,s[i].x,i);
update(1,1,mx,s[i].y,s[i].y2,s[i].x2,i);
}
for(int i=1;i<=n;i++)
ans+=(find(i)==i);
printf("%lld",ans);
return 0;
}
8.10考试总结(NOIP模拟35)[玩游戏·排列·最短路·矩形]的更多相关文章
- 6.10考试总结(NOIP模拟6)
前言 就这题考的不咋样果然还挺难改的.. T1 辣鸡 前言 我做梦都没想到这题正解是模拟,打模拟赛的时候看错题面以为是\(n\times n\)的矩阵,喜提0pts. 解题思路 氢键的数量计算起来无非 ...
- 2021.10.10考试总结[NOIP模拟73]
T1 小L的疑惑 对于\(P_i\),如果所有比\(P_i\)小的数加起来也达不到\(P_i-1\),那么值域肯定不连续.否则设原来值域最大值为\(mx\),则\(P_i\)会让值域最大值增致\(mx ...
- 5.23考试总结(NOIP模拟2)
5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...
- 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]
6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...
- noip模拟35[第一次4题·裂了]
noip模拟35 solutions 这是我第一次这么正式的考四个题,因为这四个题都出自同一个出题人,并不是拼盘拼出来的. 但是考得非常的不好,因为题非常难而且一直想睡觉.. 有好多我根本就不会的算法 ...
- 5.22考试总结(NOIP模拟1)
5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...
- Noip模拟35 2021.8.10
考试题目变成四道了,貌似确实根本改不完... 不过给了两个小时颓废时间确实很爽(芜湖--) 但是前几天三道题改着不是很费劲的时候为什么不给放松时间, 非要在改不完题的时候颓?? 算了算了不碎碎念了.. ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
- [考试总结]noip模拟23
因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...
- noip模拟35
A. 玩游戏 考场做法用双指针向两侧更新,当左段点左移一位时,如果右端点不满足条件,则跳回肯定满足的位置.复杂度玄学 题解做法是类似最长子段和,如果有一个区间和为负,则维护的指针跳过去即可 B. 排列 ...
随机推荐
- 实验k8s ————— k8s 搭建[一]
前言 以前学习k8s记录的.这里简单整理一下搭建,当时是我们学习环境的搭建,正式环境得专门的运维人员来,毕竟人家考虑的东西不一样. 正文 这里用kubeadm进行搭建,更加详细信息,在这里: http ...
- oracle 数据库连接
前言 关于oracle 数据库如何连接,我一开始以为和mysql 和 sql server一样,写好连接语句然后调用相应的dll. 知道我遇到了两个错误: 1.64位程序不能去驱动32位客户端 2.O ...
- Linux下的常见基本指令
pwd //显示当前用户所在的路径 ls //显示当前路径下的文件名或者目录名称 ls-l //显示当前路径下的文件或者目录的更详细的属性信息 cd 一个目录路径 //进入一个目录,进去后,可以用pw ...
- 实际项目中一般使用到的git知识
1.项目上线分支管理流程 图片压缩太厉害有些模糊 700k 压缩到20多k 清晰些的图片地址https://project.zdzspace.cn/test-vuekey 2.一些常用的git命令 g ...
- vue3中动态添加路由刷新无法正确匹配路由组件
1.问题 动态添加路由之后,页面重新匹配路由,匹配到了设置的404 notfound页面 该页面是在路径无法匹配到的时候才会跳转的页面 2. 问题查找 在前置路由守卫打印to 发现当前地址匹配到的组件 ...
- 力扣904(Java)-水果成篮(中等)
题目: 你正在探访一家农场,农场从左到右种植了一排果树.这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 . 你想要尽可能多地收集水果.然而,农场的主人 ...
- 牛客网-SQL专项训练7
①SQL语言可以分为多个类别,那么不属于数据操纵语言DML的是(B) 解析: SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL. 1. 数据查询语 ...
- 【pytorch学习】之自动微分
5 自动微分 求导是几乎所有深度学习优化算法的关键步骤.虽然求导的计算很简单,只需要一些基本的微积分.但对于复杂的模型,手工进行更新是一件很痛苦的事情(而且经常容易出错).深度学习框架通过自动计算导数 ...
- HarmonyOS NEXT应用开发案例——列表编辑实现
介绍 本示例介绍用过使用ListItem组件属性swipeAction实现列表左滑编辑效果的功能. 该场景多用于待办事项管理.文件管理.备忘录的记录管理等. 效果图预览 使用说明: 点击添加按钮,选择 ...
- [Contract] truffle-flattener 合并 Solidity 文件的依赖到一个文件
使用 $ npm install truffle-flattener -g $ truffle-flattener <solidity-files> > output.sol 为什么 ...