【赛时总结】NOIP2018-三校联考1024
◇NOIP三校联考-1024◇
发现以前的博客写得似乎都很水……基本上都没什么阅读量QwQ 决定改过自新╰( ̄ω ̄o) 就从这篇博客开始吧~
现场考得无地自容,看到题解才发现一些东西……(我第三题还没有做出来,反正做出来再补上)
◊ 题目& 简单解析
第一题:组合
【题目】
有n条线段,线段的两端点各有一个值(线段两端值可以相同,也可以存在端点值相同的多条线段)。如果线段A的端点的值为(a,b),线段B的端点的值为(b,c),则可以通过将A,B相接,使AB构成一条端点值为(a,c)的线段。求是否存在一种按顺序连接线段的方案,使得所有线段连成一条线段。
另外给出一个参数T,如果T=1,则可以将线段调转方向,即线段 (a,b) 可以看成 (b,a);如果T=2,则线段不能调转方向。
输入:第一行给出T和n,m,n表示线段端点的值∈[1,n],m表示共有m条线段;接下来m行每行描述一条线段(a[i],b[i])
输出:第一行输出"YES"/"NO"表示是否存在方案;若为"YES"输出任意一组方案(按连接顺序输出线段编号,若线段连接时调转方向,则输出其编号的相反数)。
【分析】
由于每条线段只能使用一次且必须使用,但是端点值可以重复多次,若将线段(a,b)看作连接a,b的边,则问题转变为求一条欧拉路径(不一定是欧拉环),而T描述的是边是有向边还是无向边(可调转方向则是无向边)。根据欧拉路径的性质——若为无向图,则度数为奇数的点的个数不能超过2,如果存在度数为奇数的点,则必须以某一个度数为奇数的点作为欧拉路径;若为有向图,则出度不等于入度的点的个数不能超过2,且如果存在入度大于出度的点,则必须以该点作为欧拉路径的起点。
根据上述特征先建图。然后判断点的度数,同时确定起点(如果无法找到起点,即上述的两种特殊情况都不存在,则形成的是欧拉回路,所以可以将任意一点作为起点(注意不要直接把1作为起点,因为数据并没有保证点1~n都出现过) 本地检测的时候SpecialJudge写得丑,然后检测器碰到这种情况自己炸了(lll¬ω¬))。然后就用到了我考试过后才学的Hierholzer算法,专门拿来求欧拉路径/欧拉回路——如果图上存在欧拉回路,则求得的就是欧拉回路,否则求得的是欧拉路径。
Hierholzer算法大概就是从欧拉路径的起点出发,DFS选择一条没有走过的边继续走,直到不能走(没有边或者边都走过)为止,当DFS回溯时,将该边入栈。最后将栈内的所有边出栈就可以得到一条欧拉路径。
当然这样求到的是图中最大的一条欧拉路径,如果欧拉路径的边数没有达到m,即没有走过图中所有的边,则输出"NO";比如 (↔表示连接)"1↔2,2↔1,3↔4,4↔3"显然无法走完整个图。
第二题:统计
【题目】
给出一个长度为n的序列a[i],对序列进行m次操作,每次操作指定一个下标i∈(1,n),对于每一个j | j≥i且a[j]≤a[i] ,将a[j]从原数组中提出(原数组中a[j]的位置留空),再对所有满足条件的a[j]单独排序,最后按顺序放入原数组的空位中。问在操作前和进行操作后数组中逆序对的数量。
eg: a = {1,3,4,2,6,1} → (i=2) → a[j] = {3(a[2]),2(a[4]),1(a[6])} → 排序 → a[j]={1,2,3} → 放入空位 → a = {1,1,4,2,6,3}
【分析】
众所周知,求逆序对除了用归并排序,还可以用树状数组。根据树状数组,我们可以求出f[i],表示在i后面的小于a[i]的数的个数(即倒序从n到1插入a[i],再询问小于a[i]的数的个数)。设操作中被选中的元素的下标集合为 pos(pos升序排列),则对于 j∈pos ,以a[j]作为较大值的逆序对仅存在 ( a[j] , a[k]|k∈pos且k>j ) ,应该很好理解吧,就不多做解释了。因此在对a[j]排序后,对于每一个 i∈pos,就不存在以a[i]为较大值的逆序对了。因此它对答案的贡献就减少了f[i],但是它的改变不会对以其他元素为较大值的逆序对的数量产生影响。
为什么不产生影响?做一个简单的解释:假设选中a[j]的j的最小值为L。
①对于L之前的数a[i],a[j]排序后仍然在a[i]的后面,且a[i]与a[j]的相对大小没有改变,因此数量不会改变;
②对于L~n之间的数a[i],满足 a[i]>a[j](操作的要求),虽然排序后a[j]的位置改变,但是仍然小于a[i],且数目不变,因此数量不会改变;
举个例子:
好了,扯到贡献了。那么就相当于每次操作后,被操作的a[j]对逆序对的贡献(以a[j]为较大值的逆序对的数量)就变为0了。可以看成把a[j]删除,但是只是删去a[j]的贡献,而在统计其他数的逆序对的时候需要统计a[j]。记最初序列中逆序对的数量为sum,那么我们每进行一次操作,就需要执行 sum-=f[j](减去a[j]的贡献),顺便删除a[j]。
删除?双向链表!从选中的下标i出发,按链表顺序遍历j,如果a[j]<=a[i],则将j的前驱接上j的后继(删除),将sum-=f[j],给f[j]赋值为0。感觉是正解对吧 QwQ?然后就发现被某chuichui tly加的一组特殊数据卡掉了……%%%
无奈写正解,好吧,其实是线段树!用线段树维护区间最小值——如果区间[i,n]的最小值都大于a[i]的话,那么这个区间就不需要操作,否则查找子区间,直到找到叶节点,就找到了小于等于a[i]的a[j],然后将a[j]改为INF,sum-=f[j]。这样虽然和链表的思路是一样的……但是时间复杂度就由 O(n) 变成了 O(log n)!挺优秀的……(●'◡'●)
(第三题还没做出来,太弱了……好了好了,粘代码了)
◊ 源代码
【第一题-merge】
/*Lucky_Glass*/
#include<bits/stdc++.h>
using namespace std;
const int N=int(2e5),M=int(1e5);
int tag,m,n,beg,cnt;
int tot[M+5],ans[N+5];
bool vis[N+5];
struct LINK{int v,id;};
vector< LINK > lnk[M+5];
void DFS(int u,int id){
for(int i=0;i<(int)lnk[u].size();i++)
if(!vis[abs(lnk[u][i].id)]){
vis[abs(lnk[u][i].id)]=true;
DFS(lnk[u][i].v,lnk[u][i].id);
}
if(id) ans[++ans[0]]=id;
}
int main(){
freopen("merge.in","r",stdin);
freopen("merge.out","w",stdout);
scanf("%d%d%d",&tag,&m,&n);
if(tag==1){
for(int i=1;i<=n;i++){
int u,v;scanf("%d%d",&u,&v);beg=u;
tot[u]^=1;tot[v]^=1;
lnk[u].push_back((LINK){v,i});
lnk[v].push_back((LINK){u,-i});
}
for(int i=1;i<=m;i++)
if(tot[i])
cnt++,beg=i;
if(cnt>2)
printf("NO\n"),exit(0);
DFS(beg,0);
}
else{
for(int i=1;i<=n;i++){
int u,v;scanf("%d%d",&u,&v);beg=v;
lnk[u].push_back((LINK){v,i});
tot[u]++;tot[v]--;
}
for(int i=1;i<=m;i++){
if(tot[i]){
cnt++;
if(tot[i]==1) beg=i;
}
}
if(cnt>2)
printf("NO\n"),exit(0);
DFS(beg,0);
}
if(ans[0]!=n)
printf("NO\n"),exit(0);
printf("YES\n");
for(int i=ans[0];i>=1;i--)
if(i==1) printf("%d",ans[i]);
else printf("%d ",ans[i]);
printf("\n");
return 0;
}
【第二题(原始数据)-count】
/*Lucky_Glass*/
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&-x)
const int N=2e5;
int n,m;
long long sum;
int tre[N+5],num[N+5],fal[N+5],pre[N+5],beh[N+5];
void Insert(int pos){
while(pos<=n)
tre[pos]++,
pos+=lowbit(pos);
}
int Query(int pos){
int ret=0;
while(pos)
ret+=tre[pos],
pos-=lowbit(pos);
return ret;
}
int main(){
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
pre[i]=i-1;
beh[i]=i+1;
}
for(int i=n;i>=1;i--){
fal[i]=Query(num[i]-1);
sum+=fal[i];
Insert(num[i]);
}
printf("%lld",sum);
for(int i=1;i<=m;i++){
int pos;scanf("%d",&pos);
if(!fal[pos]){
printf(" %lld",sum);
continue;
}
for(int j=pos;j<=n;j=beh[j])
if(num[j]<=num[pos]){
beh[pre[j]]=beh[j];
pre[beh[j]]=pre[j];
sum-=fal[j];
fal[j]=0;
}
printf(" %lld",sum);
}
printf("\n");
return 0;
}
【第二题(额外数据)-count】
/*Lucky_Glass*/
#include<bits/stdc++.h>
using namespace std;
const int N=2e5;
struct TREEARRAY{
#define lowbit(x) (x&-x)
int tre[N+5];
TREEARRAY(){memset(tre,0,sizeof tre);}
void Insert(int pos,int n){
while(pos<=n)
tre[pos]++,
pos+=lowbit(pos);
}
int Query(int pos){
int ret=0;
while(pos)
ret+=tre[pos],
pos-=lowbit(pos);
return ret;
}
}ary;
struct SEGTREE{
struct NODE{
int l,r,num;
}tre[N*5];
void Update(int u){
tre[u].num=min(tre[u<<1].num,tre[u<<1|1].num);
}
void Init(int a[],int l,int r,int u){
tre[u].l=l;tre[u].r=r;
if(l==r){
tre[u].num=a[l];
return;
}
int mid=(l+r)>>1;
Init(a,l,mid,u<<1);Init(a,mid+1,r,u<<1|1);
Update(u);
}
void Query(int u,int l,int val,long long &sum,int f[]){
if(tre[u].r<l || tre[u].num>val) return;
if(tre[u].l==tre[u].r){
sum-=f[tre[u].l],tre[u].num=(1<<29);
return;
}
int mid=(tre[u].l+tre[u].r)>>1;
if(l>=mid+1) Query(u<<1|1,l,val,sum,f);
else{
Query(u<<1,l,val,sum,f);
Query(u<<1|1,l,val,sum,f);
}
Update(u);
}
}seg;
int n,m;
long long sum;
int f[N+5],num[N+5];
int main(){
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
for(int i=n;i>=1;i--){
sum+=(f[i]=ary.Query(num[i]-1));
ary.Insert(num[i],n);
}
seg.Init(num,1,n,1);
printf("%lld",sum);
for(int i=1;i<=m;i++){
int pos;scanf("%d",&pos);
seg.Query(1,pos,num[pos],sum,f);
printf(" %lld",sum);
}
return 0;
}
The End
Thanks for reading!
- Lucky_Glass
【赛时总结】NOIP2018-三校联考1024的更多相关文章
- 三校联考 Day3
三校联考 Day3 大水题 题目描述:给出一个圆及圆上的若干个点,问两个点间的最远距离. solution 按极角排序,按顺序枚举,显然距离最远的点是单调的,线性时间可解出答案. 大包子的束缚 题目描 ...
- HGOI20180823 三校联考
首测:220qwq(算差的好吧) 后来改了一个地方:300qwq(算慢的好吧) std被踩qwq 注意:输入数据第一行忘记输入n,亲脑补 题解: 多项式除法(若最后除出的答案为1那么就是成功),对于f ...
- 【五校联考1day2】JZOJ2020年8月12日提高组T2 我想大声告诉你
[五校联考1day2]JZOJ2020年8月12日提高组T2 我想大声告诉你 题目 Description 因为小Y 是知名的白富美,所以自然也有很多的追求者,这一天这些追求者打算进行一次游戏来踢出一 ...
- [2019多校联考(Round 6 T3)]脱单计划 (费用流)
[2019多校联考(Round 6 T3)]脱单计划 (费用流) 题面 你是一家相亲机构的策划总监,在一次相亲活动中,有 n 个小区的若干男士和 n个小区的若干女士报名了这次活动,你需要将这些参与者两 ...
- [多校联考2019(Round 5 T1)] [ATCoder3912]Xor Tree(状压dp)
[多校联考2019(Round 5)] [ATCoder3912]Xor Tree(状压dp) 题面 给出一棵n个点的树,每条边有边权v,每次操作选中两个点,将这两个点之间的路径上的边权全部异或某个值 ...
- [多校联考2019(Round 5 T2)]蓝精灵的请求(二分图染色+背包)
[多校联考2019(Round 5)]蓝精灵的请求(二分图染色+背包) 题面 在山的那边海的那边住着 n 个蓝精灵,这 n 个蓝精灵之间有 m 对好友关系,现在蓝精灵们想要玩一个团队竞技游戏,需要分为 ...
- [多校联考2019(Round 5 T3)]青青草原的表彰大会(dp+组合数学)
[多校联考2019(Round 5)]青青草原的表彰大会(dp+组合数学) 题面 青青草原上有n 只羊,他们聚集在包包大人的家里,举办一年一度的表彰大会,在这次的表彰大会中,包包大人让羊们按自己的贡献 ...
- ZR10.1青岛集训三地联考
ZR10.1青岛集训三地联考 谢谢dijk和smy A 题目大意: 已知斐波那契数列\(f\) 设 \[ F_i = \sum_{i = 0}^nf_if_{n - i} \] 求 \[ \sum_{ ...
- 【五校联考1day2】JZOJ2020年8月12日提高组T1 对你的爱深不见底
[五校联考1day2]JZOJ2020年8月12日提高组T1 对你的爱深不见底 题目 Description 出乎意料的是,幸运E 的小R 居然赢了那个游戏.现在欣喜万分的小R 想要写一张明信片给小Y ...
随机推荐
- vue-cli脚手架和webpack-simple模板项目
vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目. GitHub地址是:https://github.com/vuejs/vue-cli 一. ...
- 如何学习Java?学习Java顺序?
Java相对于Asp.Net或Asp.Net MVC来讲,入门是比较困难和烦琐的!它不像.Net哪样有安装开发工具就可以跑程序了,不需要配置复杂的运行环境. 推荐的学习Java的学习顺序如下: 一.J ...
- .NET开源工作流RoadFlow-表单设计-组织机构选择
组织机构选择即在表单中添加组织机构选择框. 选择范围: 1.发起者部门:只能在发起者同一个部门中选择. 2.处理者部门:只能在当前处理者同一个部门中选择. 3.自定义:自己指定一个选择范围. 选择类型 ...
- wx.grid 简单例子
import wx, wx.grid class GridData(wx.grid.PyGridTableBase): _cols = "a b c".split() _data ...
- Java常用Json库性能对比
Java对于处理JSON数据的序列化与反序列化目前常用的类库有Gson.FastJSON.Jackson.jettison以及json-lib.在这里我们将对这些类库在json序列化与反序列化方面的性 ...
- MonkeyRunnermac
http://blog.csdn.net/wyb199026/article/details/51436754 背景 之前想学习自动化测试的时候,就研究过MonkeyRunner,说实话MonkeyR ...
- SpringMvc-reset风格
reset风格就是不用加?或者&等等符号直接通过地址栏向后台发送数据的方法 rest风格是实现 1.需要在web.xml中配置一个filter <filter> <filte ...
- FIFO认识(一)
1.什么是FIFO? FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据 ...
- 【2017-06-06】Qt中的键盘事件:QKeyEvent及相关处理函数
QKeyEvent及其相关的处理函数,比如QKeyPressEvent.QKeyReleaseEvent()等在QtGui编程中非常常用,尤其是需要借助标准键盘的编码来实现一些自定义功能的时候. 这算 ...
- March 26 2017 Week 13 Sunday
Deliver not your words by number but by weight. 言不在多,而在有物. Do more than talk, say something. I still ...