首页 > 其他分享 >20230707-NOIP模拟赛(多校联训)

20230707-NOIP模拟赛(多校联训)

时间:2023-07-08 09:55:25浏览次数:54  
标签:20230707 NOIP int vis 联训 maxn freopen dp dis

20230707

T1.信号传输(signal)

考场思路

先把这\(n+k+1\)个点都转化到平面直角坐标系上面

又是没有想清楚就开始打代码(但至少比昨天好,懂得放弃)
本来想的是按照x轴从左到右扫一遍
每一次处理这一列上的每个点
复杂度是\(O(n)\)
但是后面想到有可能信号是从后面的点传送过来的
所以我又再从右到左扫一遍
最后测试样例时发现这个想法是错误的
如果要全部遍历完需要不断地扫
因为这道题每条边的代价都是1

所以我果断放弃
在点之间建边,复杂度\(O(n^2)\)
用digstra打了30分的暴力

Solution

发现对于横坐标或者纵坐标相等的点
再那一条线上是可以随便传到任意一个点上的
所以或许可以把再这样一条平行于坐标轴的点看做一个整体
而每个点的作用就是转弯90度
也就是把它所在的两条直线(分别平行于x轴和y轴)
建立一条边连起来
而对于最后的\(k\)个点,就不用建边
这样在查询时讨论信号是从哪条直线来即可

这样就相当于在我的暴力上面进行优化
把点数和边数都减小了

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+10,inf=0x3f3f3f3f,mx=2e6+1;
int n,k,m,head[maxn<<2],tot=0,dis[maxn<<2],x;
bool vis[maxn<<2];
struct edge{
  int v,nxt,val;
}e[maxn<<3];
struct node{
  int x,y;
}a[maxn];

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

void add(int u,int v){
  dis[u]=dis[v]=inf;
  e[++tot]=(edge){v,head[u],1};
  head[u]=tot;
  e[++tot]=(edge){u,head[v],1};
  head[v]=tot;
}

priority_queue<pair<int,int> > q;
void dijstra(){
  memset(vis,false,sizeof(vis));
  dis[a[0].x]=0;dis[a[0].y+mx]=0;
  q.push(make_pair(0,a[0].x));
  q.push(make_pair(0,a[0].y+mx));
  while(!q.empty()){
  	int u=q.top().second;q.pop();
  	if(vis[u]) continue;
  	vis[u]=true;
  	for(int i=head[u];i;i=e[i].nxt){
  	  int v=e[i].v,val=e[i].val;
	  if(dis[v]>dis[u]+val){
	  	dis[v]=dis[u]+val;
	  	q.push(make_pair(-dis[v],v));
	  }	
	}
  }
}

int main(){
  freopen("signal.in","r",stdin);
  freopen("signal.out","w",stdout);
  memset(dis,-1,sizeof(dis));
  n=read();k=read();
  for(int i=0;i<=n;i++){
  	a[i].x=read();a[i].y=read();
    add(a[i].x,a[i].y+mx);
  }
  for(int i=1;i<=k;i++) a[i+n].x=read(),a[i+n].y=read();
  dijstra();
  m=read();
  for(int i=1;i<=m;i++){
  	x=read();int ans=inf;
  	if(dis[a[x].x]==-1&&dis[a[x].y+mx]==-1) printf("-1\n");
  	else{
  	  if(dis[a[x].x]!=-1) ans=min(ans,dis[a[x].x]);
  	  if(dis[a[x].y+mx]!=-1) ans=min(ans,dis[a[x].y+mx]);
  	  printf("%d\n",ans+2);
	}
  }
  return 0;
}

注意dijstra复杂度:\(O(m log n)\)

T2.重排序列(permutation)

考场思路

把题意理解错的还得是我
可能还是因为没有想清楚
然后写了一个自认为的网络流的60分的暴力
打完了才发现题目要求是字典序最大
而不是让你序列总和最大

于是我就又打了个\(O(n^2)\)的暴力
结果答案错误就只有10分

Solution

原题CF313E

首先在读入的时候取模(这个不用说都知道)
然后,我们得到的\(c_i\)就有两种情况
\(a[i]+b[j] \lt m\)或者是\(a[i]+b[j]-m\)
很显然,第二种是不划算的
那么我们就考虑让第一种越多越好

我们将\(a,b\)数组从小到大排序
再从小到大去枚举\(a\)
我们去找与它加起来最大的\(b[j]\)使两者相加小于\(m\)
那这样找到的\(b[j]\)一定是一段后缀
因为\(b[j]\)要么不匹配,要么和比\(a[i]\)更小的匹配
这样肯定都不是最优

但是按照这样实现就在\(a[i]\)把\(a[i-1]\)淘汰之后
我们没有办法再去找\(a[i-1]\)的匹配
所以这样写正确性是不能保证的(大样例过不了)

所以我们想要从大到小枚举数组\(b\)
然后找到第一个与它相加\(\lt m\)的\(a[i]\)
所以我们贪心地去匹配就可以了

而对于最后剩下来的
就是他们相加大于\(m\)的了
那我们就让大的和大的匹配从而让字典序更大即可

说起来确实听简单,但是想不到啊……

#include <bits/stdc++.h>
using namespace std;

const int maxn=5e5+10;
int n,mod,a[maxn],b[maxn],c[maxn],cnt=0,it;
bool vis[maxn];

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
} 

multiset<int> st;

int main(){
  freopen("permutation.in","r",stdin);
  freopen("permutation.out","w",stdout);
  n=read();mod=read();
  for(int i=1;i<=n;i++) a[i]=read(),a[i]%=mod,st.insert(a[i]);
  for(int i=1;i<=n;i++) b[i]=read(),b[i]%=mod;
  sort(b+1,b+n+1);
  for(int i=n;i>=1;i--){
  	multiset<int>::iterator it=st.lower_bound(mod-b[i]);
  	if(it==st.begin()) continue;
	c[++cnt]=b[i]+(*--it);
  	st.erase(it);vis[i]=true;
  }
  for(int i=1;i<=n;i++){
  	if(vis[i]) continue;
  	c[++cnt]=b[i]+*st.begin()-mod;
  	st.erase(st.begin());
  }
  sort(c+1,c+cnt+1);
  for(int i=cnt;i>=1;i--) printf("%d ",c[i]);
  printf("\n");
  return 0;
}

T3.价格调查(survey)

考场思路

写了一个\(n^3\)的dp
想要骗个10分但是写挂了

Solution

原题ARC159D

把题意转化就是一个难点
就如原题里面说的
每一次我们加入\(l \sim r\)的每一个元素
最后求最长上升子序列

我们考虑选择一个区间
就一定要选它的右端点,否则不优
所以说不同的右端点只有\(n\)个

所以令\(dp_i\)表示以\(i\)结尾的最长上升子序列
那么在加入区间\([l,r]\)时,我们只用更新\(dp_r\)

这样就可以分两种情况:
\(i \lt l,dp_r=dp_i+r-l+1\)
\(l \le i \lt r,dp_r=dp_i+r-i\)

这样的\(dp\)很显然是\(O(n^2)\)的
所以我们考虑当我们选\(dp_i\)时一定是选的小于\(l\)中最大的
我们可以用线段树来维护
同理,对于第二种情况的\(dp_i-i\)也可以用线段树来维护

这道题的关键就在于分析到必须要选右端点
否则肯定不是最优的

#include <bits/stdc++.h>
using namespace std;
#define mid L+((R-L)>>1)
#define lson L,mid,rt<<1
#define rson mid+1,R,rt<<1|1
#define lb(x) lower_bound(a+1,a+len+1,x)-a

const int maxn=2e5+10,inf=0x3f3f3f3f;
int a[maxn<<1],l[maxn],r[maxn],n,dp[maxn<<1],tr[2][maxn<<3],len,ans=0;

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
} 

void pushup(int u,int rt){tr[u][rt]=max(tr[u][rt<<1],tr[u][rt<<1|1]);}

void update(int u,int x,int val,int L=1,int R=len,int rt=1){
  if(L==R){
  	tr[u][rt]=val;
  	return ;
  }
  if(x<=mid) update(u,x,val,lson);
  else update(u,x,val,rson);
  pushup(u,rt);
}

int query(int u,int x,int y,int L=1,int R=len,int rt=1){
  if(x>y) return 0;
  if(x<=L&&y>=R) return tr[u][rt];
  int res=-inf;
  if(x<=mid) res=max(res,query(u,x,y,lson));
  if(y>mid) res=max(res,query(u,x,y,rson));
  return res;
}

void build(int L=1,int R=len,int rt=1){
  tr[0][rt]=0;
  if(L==R){
  	tr[1][rt]=-a[L];
  	return;
  }
  build(lson);build(rson);
  pushup(1,rt);
}

int main(){
  freopen("survey.in","r",stdin);
  freopen("survey.out","w",stdout);
  n=read();
  for(int i=1;i<=n;i++) l[i]=read(),r[i]=read(),a[i]=l[i],a[i+n]=r[i];
  sort(a+1,a+n*2+1);
  len=unique(a+1,a+n*2+1)-a-1;
  build();
  for(int i=1;i<=n;i++){
  	int p=lb(r[i]),p1=lb(l[i]);
  	dp[p]=max(query(0,1,p1-1)+r[i]-l[i]+1,query(1,p1,p)+r[i]);
  	update(0,p,dp[p]);update(1,p,dp[p]-r[i]);
  	
  }
  for(int i=1;i<=len;i++) ans=max(ans,dp[i]);
  printf("%d\n",ans);
  return 0;
}

T4.哑剧(mime)

考场思路

用了一个数组,和一棵线段树
甚至不是可持久化
乱搞\(O(n^2 log n)\)
大样例都没过还有50分

Solution

还是没看懂
好像用了分块

总结

要学会对题目的特殊性质进行分析
不断优化解题过程,可以尝试合并之类的做法

标签:20230707,NOIP,int,vis,联训,maxn,freopen,dp,dis
From: https://www.cnblogs.com/hewanying0622/p/17535346.html

相关文章

  • 20230707巴蜀暑期集训测试总结
    T1SPFA就能过!给我震惊到了。可以斜率优化。对每个站点维护一个凸包。\[f(x)=Ax^2+Bx+C\\dp_{v,q}=\min_{i=0}^{p}\{dp_{u,i}+f(p-i)\}\\(i,dp_{x,i}+Ai^2-Bi)\]T2考场想了想区间dp,有点思路但是时间不多了有点慌,打个暴搜直接跑。相当于将位置当作第二关键字比较,最大的数......
  • 「NOIP 模拟赛 20230707」T2 - 涂照片 题解
    题目大意原题有一个\(n+1\timesm+1\)的网格。对于每一行\(i\),都要将左侧的一些格子\((i,1),(i,2),\ldots,(i,x)\)涂黑,其中\(x=k\)的概率为\(a_{i,k}\)。同理对于每一列\(j\),都要将上方的一些格子\((1,j),(2,j),\ldots,(x,j)\)涂黑,其中\(x=k\)的概率为\(b_{k,......
  • 20230706-NOIP模拟赛
    20230706T1.骰子游戏(dice)题目大意给你两个正整数\(n\)和\(d\),你需要构造\(n\)组数据,每组6个整数满足整数都在\([0,10^6]\)范围内,每组数据中两两不同,在每组数据中分别随机选一个数所得到的异或和为\(d\)的倍数如果能构造出这样的\(n\)组数据,请先输出‘Yes’,随后输......
  • 20230707-编程语言的变量覆盖
    实现一个特性时,发现自定义的变量position覆盖了类的属性Position,近期发现始终存在的一个难以复现的窗口还原 BUG可能被因此修复了。也曾Debug过,但没能复现。问题的解决就是这样,只要你还惦记着,问题总会被解决。对于大小写不敏感度编程语言,尤其要注意大小写,所以我和我的朋......
  • 成语积累 20230707
    璞玉浑金:璞玉:未经人工雕琢的玉;浑金:没有冶炼过的金子。比喻人的品质纯美质朴,或指天然浑朴的精美之器。多用来形容人的品质淳朴善良。例句:这个小姑娘来自四川偏远的山区,给人一种~的感觉。例句2:现在的人都太现实,那种~的人基本很难找到了。假途灭虢:假:借;途:道路;灭:消灭;虢:春秋时诸侯国。......
  • P8182 「EZEC-11」雪的魔法 / NOIP 模拟赛 20230706 D 思考--zhengjun
    引用:这是一道非常棒的思维题,可以说没有用到任何高深的知识点,却极大地考验了做题人的思维能力和创造性。本题分为两步。根据线性规划对偶或贪心,转化题意。对\(m\)根号分治,然后分别进行分治。\(m\le\sqrt{n}\)分治比较好想,\(m>\sqrt{n}\)的根号分治比较难想。这......
  • QOJ 5500. Bars / NOIP 模拟赛 20230706 B 进阶版--zhengjun
    本题转化为梯形面积就已经不是很好想了(赛时切掉,开心!)进阶为静态区间查询。使用不删除莫队+凸包合并凸包合并就是把散块和整块的凸包合并注意这里两个凸包的横坐标值域是无交的于是可以使用二分套二分解决此问题代码咕着,感觉非常难写......
  • 「NOIP 模拟赛 20230706」轨道飞跃
    summarizationsolution考虑倒着走,那么从\(u\)走到\(v\)条件就变为\(r_v\)在\(u\)所在的区间内,于是我们预处理出右端点在这段区间内的轨道的左端点的最小值(可以用ST表),然后从\(t\)跳回\(s\)。不难发下,往回跳的过程可以用倍增优化(具体见代码),最终复杂度\(\mathcal{O......
  • 「NOIP 模拟赛 20230705」序列删数问题
    summarizationsolution首先发现,范围小的工具在删除某一数字时将更大数字包括进来的可能性越小,可以删除的数更多,所以在删除某一数字时应该尽可能选择范围较大的工具。接下来我们考虑可删数(要删除的数)删除的顺序:考虑要删掉每个数所允许的最大的工具的区间长度。现在假设有两个......
  • 「NOIP 模拟赛 20230706」偷 WiFi
    summarization有一个长度为\(n\)的序列\(p\),将其中若干个数标记。对于序列中的每一个位置\(i\),其贡献为其左边与右边离它最近的被标记的数的数值的和。求出最大的贡献总和。(\(1\len\le2\times10^6\))solution首先显然,\(p_1,p_n\)一定要标记。然后考虑分别求相邻的标记数......