所谓人,无论是谁到了最后,都会形单影只。

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)[玩游戏·排列·最短路·矩形]的更多相关文章

  1. 6.10考试总结(NOIP模拟6)

    前言 就这题考的不咋样果然还挺难改的.. T1 辣鸡 前言 我做梦都没想到这题正解是模拟,打模拟赛的时候看错题面以为是\(n\times n\)的矩阵,喜提0pts. 解题思路 氢键的数量计算起来无非 ...

  2. 2021.10.10考试总结[NOIP模拟73]

    T1 小L的疑惑 对于\(P_i\),如果所有比\(P_i\)小的数加起来也达不到\(P_i-1\),那么值域肯定不连续.否则设原来值域最大值为\(mx\),则\(P_i\)会让值域最大值增致\(mx ...

  3. 5.23考试总结(NOIP模拟2)

    5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...

  4. 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]

    6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...

  5. noip模拟35[第一次4题·裂了]

    noip模拟35 solutions 这是我第一次这么正式的考四个题,因为这四个题都出自同一个出题人,并不是拼盘拼出来的. 但是考得非常的不好,因为题非常难而且一直想睡觉.. 有好多我根本就不会的算法 ...

  6. 5.22考试总结(NOIP模拟1)

    5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...

  7. Noip模拟35 2021.8.10

    考试题目变成四道了,貌似确实根本改不完... 不过给了两个小时颓废时间确实很爽(芜湖--) 但是前几天三道题改着不是很费劲的时候为什么不给放松时间, 非要在改不完题的时候颓?? 算了算了不碎碎念了.. ...

  8. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

  9. [考试总结]noip模拟23

    因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...

  10. noip模拟35

    A. 玩游戏 考场做法用双指针向两侧更新,当左段点左移一位时,如果右端点不满足条件,则跳回肯定满足的位置.复杂度玄学 题解做法是类似最长子段和,如果有一个区间和为负,则维护的指针跳过去即可 B. 排列 ...

随机推荐

  1. Django3.0连接数据库注意点

    需先在应用下的__Init__.py文件中配置 import pymysqlpymysql.version_info=(1, 3, 13, 'final', 0) # 3.0时需要pymysql.in ...

  2. 8k中英双语文本嵌入模型效果初探

    一 模型介绍 向量模型用于生成向量表示,被广泛应用于检索.分类.聚类或语义匹配等传统的自然语言处理任务.到了大模型时代,由于上下文长度的限制,需要压缩.存储和查询大量的信息,这就需要用到向量模型对输入 ...

  3. 力扣455(java&python)-分发饼干(简单)

    题目: 假设你是一位很棒的家长,想要给你的孩子们一些小饼干.但是,每个孩子最多只能给一块饼干. 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸:并且每块饼干 j,都有 ...

  4. 全链路灰度新功能:MSE上线配置标签推送

    简介: 微服务场景下,全链路灰度作为一种低成本的新功能验证方式,得到了越来越广泛的应用.除了微服务实例和流量的灰度,微服务应用中的配置项也应该具备相应的灰度能力,以应对灰度应用对特殊配置的诉求. 为什 ...

  5. 用了那么久的Lombok,你知道它的原理么?

    简介: 在写Java代码的时候,最烦写setter/getter方法,自从有了Lombok插件不用再写那些方法之后,感觉再也回不去了,那你们是否好奇过Lombok是怎么把setter/getter方法 ...

  6. 技术揭秘:实时数仓Hologres如何支持超大规模部署与运维

    ​简介:在本次评测中,Hologres是目前通过中国信通院大数据产品分布式分析型数据库大规模性能评测的规模最大的MPP数据仓库产品.通过该评测,证明了阿里云实时数仓Hologres能够作为数据仓库和大 ...

  7. 开源 Serverless 里程碑:Knative 1.0 来了

    ​简介:近期Knative发布了1.0版本,达到了一个重要的里程碑.Knative自2018年7月首次发布以来, 版本不断的迭代发展,除了无数的错误修复.稳定性和性能增强之外,按时间顺序还进行了一些改 ...

  8. 谈谈C++新标准带来的属性(Attribute)

    简介: 从C++11开始,标准引入了一个新概念"属性(attribute)",本文将简单介绍一下目前在C++标准中已经添加的各个属性以及常用属性的具体应用. 作者 | 寒冬来源 | ...

  9. 阿里云边缘云ENS再升级 四大场景应用加速产业数字化落地

    ​简介: 云栖大会 | 于10月21日上午举办的边缘云应用升级与技术创新论坛中,阿里云边缘云ENS产品全面升级,从边缘云产品.技术.行业应用等维度全面阐述阿里云在边缘计算领域的技术积累.产品& ...

  10. Serverless 工程实践 | Serverless 应用开发观念的转变

    ​简介: Serverless 架构带来的除了一种新的架构.一种新的编程范式,还包括思路上的转变,尤其是开发过程中的一些思路转变.有人说要把 Serverless 架构看成一种天然的分布式架构,需要用 ...