两个水题一个神题。

woj4777~4779

光线追踪

这个题,,一看就跟oil很像啊,,。

一个矩形有效的部分只有左边的和下边的两条线段。另外两条撞不到。所以拆分线段,维护x和y轴两个线段树,下标为离散化后的斜率(可以用map),然后查询对应斜率的x和y,用查出来的x*斜率得到y'和y比较就行。

注意:如果查询是垂直或者平行直线,就要看一下。选最近的,或者id小的(因为id小的没有被覆盖只能说明更近),然后就没了。

细节小心一点就行。我由于没有小心挂成了30分。

#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{int cnt=0,f=1;char ch=0;while(!isdigit(ch)){ch=getchar();if(ch=='-')f=-1;}while(isdigit(ch)){cnt=cnt*10+ch-48;ch=getchar(); }return cnt*f;
}
struct node{int l,r;pair<int,int> key;
}t[2][500003];//0横着,y竖着。
struct bili{int op,x,y,xx,yy;//x<xx,y<yy;
}que[100003];
int q;double b[400003];int bcnt;
map<double,int> M;pair<double,int> ans0,ans1;
double slo(int x,int y){if(x)return (double)(y/(double)x);else return 0x3f3f3f3f;}
void build(int u,int l,int r){t[0][u].l=t[1][u].l=l;t[0][u].r=t[1][u].r=r;t[0][u].key.first=t[1][u].key.first=0x3f3f3f3f;if(l==r)return;int mid=(l+r)>>1;build(u*2,l,mid);build(u*2+1,mid+1,r);
}
void add(int u,int ql,int qr,int key,int id,int p){if(t[p][u].l>qr||t[p][u].r<ql)return;if(ql<=t[p][u].l&&t[p][u].r<=qr){if(key<=t[p][u].key.first)t[p][u].key=make_pair(key,id);return;}add(u*2,ql,qr,key,id,p);add(u*2+1,ql,qr,key,id,p);
}
void query(int u,int pos){if(t[0][u].key.first<ans0.first)ans0=t[0][u].key;else if(t[0][u].key.first==ans0.first)ans0.second=max(ans0.second,t[0][u].key.second);if(t[1][u].key.first<ans1.first)ans1=t[1][u].key;else if(t[1][u].key.first==ans1.first)ans1.second=max(ans1.second,t[1][u].key.second);if(t[0][u].l==t[0][u].r)return;int mid=(t[0][u].l+t[0][u].r)>>1;if(pos<=mid)query(u*2,pos);else query(u*2+1,pos);
}
bool cm(double a,double b){return a>b;
}
signed main(){//freopen("raytracing.in","r",stdin);//freopen("raytracing.out","w",stdout);q=in;int op,x,y,xx,yy;for(int i=1;i<=q;i++){op=in;if(op==1){x=in;y=in;xx=in;yy=in;que[i]={op,x,y,xx,yy};b[++bcnt]=slo(x,y);b[++bcnt]=slo(x,yy);b[++bcnt]=slo(xx,y);  }else{x=in;y=in;que[i]={op,x,y,0,0};b[++bcnt]=slo(x,y);}}sort(b+1,b+bcnt+1,cm);bcnt=unique(b+1,b+bcnt+1)-b-1;//逆序是保证M的顺序从上到下从左到右递增,便于线段树理解。 //cout<<bcnt<<endl;//for(int i=1;i<=bcnt;i++)printf("%.2lf ",b[i]);cout<<endl;for(int i=1;i<=bcnt;i++)M[b[i]]=i;build(1,1,bcnt);for(int i=1;i<=q;i++){double Slo=slo(que[i].x,que[i].y);if(que[i].op==1){add(1,M[slo(que[i].x,que[i].y)],M[slo(que[i].xx,que[i].y)],que[i].y,i,0);add(1,M[slo(que[i].x,que[i].yy)],M[slo(que[i].x,que[i].y)],que[i].x,i,1);//注意谁先谁后** }else{ans0=ans1=make_pair(0x3f3f3f3f,0);query(1,M[Slo]);//printf("%d %d %d %d\n",ans0.first,ans0.second,ans1.first,ans1.second);if(ans0.second==0||ans1.second==0){printf("%d\n",ans0.second^ans1.second);}else{if(Slo==0){//只有在相等的时候才会走后面。否则会弄出y对应的那个x。 if(que[ans0.second].x<que[ans1.second].x||ans0.second<ans1.second)printf("%d\n",ans0.second);else printf("%d\n",ans1.second);continue; }if(Slo==0x3f3f3f3f){if(que[ans0.second].y<que[ans1.second].y||ans0.second<ans1.second)printf("%d\n",ans0.second);else printf("%d\n",ans1.second);continue;}if(ans0.first==Slo*ans1.first){printf("%d\n",max(ans0.second,ans1.second));}if(ans0.first<Slo*ans1.first){printf("%d\n",ans0.second);}if(ans0.first>Slo*ans1.first){printf("%d\n",ans1.second);}}}}//nlognreturn 0;
} 

雪人

求两个长度相同,每一个对应位置差都一样的数列,使得min((位置差),长度)最大。

一看这个minmax,我们可以二分。(这是思维习惯了,,)

然后我就想,二分啥呢,,

二分位置差好像不行,,n方以上的鸭子。

二分长度,,诶好像可以。我如果提取所有当前长度的字符串出来比较。

但是不能一位一位比较,就开始观察性质。

使用瞪眼法我们发现,如果两个串,任意两个相邻位的差分值相等,就是可以弄的。反正我只在乎相对高度。

那我使用差分作为哈希权值可以么。

那这道题就完了,nlog^2n,。只要看每次弄出来的合法位置差最大值是否大于长度就行。

不过注意哦,开头的值也要算一个差分,,二分的时候mid-1作为差分长度,弄出来mid就是实际长度。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{int cnt=0,f=1;char ch=0;while(!isdigit(ch)){ch=getchar();if(ch=='-')f=-1;}while(isdigit(ch)){cnt=cnt*10+ch-48;ch=getchar(); }return cnt*f;
}
const int N=500003,jz=13;
int id[N];
unsigned long long Hash[N],now[N],fac[N],a[N];
const unsigned long long gu=998244353;
bool cm(int a,int b){if(now[a]!=now[b])return now[a]<now[b];return a<b;//哈希相同,长度单增,获得最长长度差。
}//字符串的题当然是要哈希灵活解决了。
signed main(){//freopen("snowman.in","r",stdin);//freopen("snowman.out","w",stdout);fac[0]=1;int n=in;for(register int i=1;i<=n;i++){a[i]=in;fac[i]=fac[i-1]*jz;Hash[i]=Hash[i-1]*fac[1]+a[i]-a[i-1]+gu; }int l=0,r=n/2+1;while(l<r){int mid=l+r+1>>1;int flag=0;for(register int i=1,j=mid-1;j<=n;i++,j++){now[i]=Hash[j]-Hash[i-1]*fac[j-i+1],id[i]=i;}sort(id+1,id+n-mid+3,cm);for(register int i=1,j=1;i<=n-mid+2;i=j){while(now[id[j]]==now[id[i]]&&j<=n-mid+2)++j;int len=id[j-1]-id[i];if(len>=mid){flag=1;break;}}if(flag)l=mid;else r=mid-1;}cout<<l;//(2/n+3n/4+7n/8+……)([共logn项]<nlogn)*logn(sort) =nlog^2nreturn 0;
}

重排

warning,神题来了。本题证明来源于zxyoi_dreamer。

每次打乱所有出边,,然后走。

概率和期望,,期望一般反推。dfs从T开始往S回溯应该是基本框架。

思维过程:

应该记录一个是否到达终点的bool,如果到不了,期望值就是inf。

然后,对于一个点,它有一堆出点,一堆出边(当然还有自环)

还有自环,,日。这个怎么搞。自环和边重排使得这道题瞬间GG。因为这是最短路,取最小操作,,高斯消元什么的都GG了更别说数据范围还不太接受。

拆分问题思想:先不管自环。

神点之一:如何处理没有自环的下一条最短路的期望和

代码中的calc函数。

走这一步的期望是边权+v的期望。

将每一条出边,每一个出点用两个数组装下来sort。然后考虑任何一种配对成为最小期望的概率。

我们考虑这么一个数列:

将两个序列如此放置。

然后我们假设选择第v条边,第pos个点,都是从左往右数的。我们发现,v左边的,和pos右边的都不能用。因为如果可以用,就存在更小的。那这条边就不是最短路。如图

然后我们开始考虑一个事情,对于一个pos和v,如果长成这个样子,最后就一定能找到一条形如穿过这条线的,两个x相连的这种边。

我们发现,上面的x比a小,下面的x比b小,那我们本来的这种选择,一定不会成为最短路。

也就是说,对于一个pos,我们只能在a数组中选择,左边的那一部分。

同理,对于v来说,只能选择右边的那一部分。

两个部分一交集,我们发现,只有中间的pos-v+1个可以用。其中有1种是我们需要的。

而为了计算最开始所说的,v左边,pos右边不能用的,我们开一个全局概率带着走,记录剩下这一坨有多少概率。

优先队列巧妙实现以下,,比较nb。

神点之二:如何处理自环

zxy说:对于这种题,我们感性理解一下。

如果可以到终点,那答案肯定是存在的。答案既然是存在的,就说明期望是唯一的,期望是唯一的,说明我可以朝这个方向逼近。也就是说,这个东西收敛。

那我们迭代一下,假设当前点的答案,然后,,二分,,

就一定能找到答案。

(日,真感性)

#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{int cnt=0,f=1;char ch=0;while(!isdigit(ch)){ch=getchar();if(ch=='-')f=-1;}while(isdigit(ch)){cnt=cnt*10+ch-48;ch=getchar();}return cnt*f;
}
int first[1003],nxt[1003],to[1003];double w[1003];
int toT[1003];double E[1003];
int cnt,res[1003],vis[1003];
double a[1003],b[1003];
int n,m,S,T;
struct node{int v;double w;};
priority_queue<node> q;
bool operator <(node a,node b){return a.w>b.w;
}
double calc(){double sum=0.0;while(!q.empty())q.pop();for(int i=1;i<=cnt;i++)q.push((node){i,a[i]+b[1]}),res[i]=cnt;double p=1;while(!q.empty()&&p>0){node u=q.top();q.pop();int v=u.v;double w=u.w;double pp=p*(res[v]-v)/(res[v]-v+1);//不选的概率?res[v]--;sum+=w*(p-pp);p=pp;if(res[v]>=1)q.push((node){v,a[v]+b[cnt-res[v]+1]}) ;}return sum;
}const double eps=1e-10;
void dfs(int u){//cout<<u<<endl;if(vis[u])return;vis[u]=1;if(u==T){vis[u]=1;toT[u]=1;return;}vis[u]=1;for(int i=first[u];i;i=nxt[i]){int v=to[i];if(!vis[v])dfs(v);toT[u]|=toT[v];}if(!toT[u])return (void)(E[u]=1e10);int flag=0;cnt=0;for(int i=first[u];i;i=nxt[i])a[++cnt]=w[i],flag|=to[i]==u;sort(a+1,a+cnt+1);if(!flag){cnt=0;for(int i=first[u];i;i=nxt[i]){int v=to[i];b[++cnt]=E[v];}sort(b+1,b+cnt+1);E[u]=calc();return;}double l=0,r=1e6;while(l+eps<r){E[u]=(l+r)*0.5;cnt=0;for(int i=first[u];i;i=nxt[i]){int v=to[i];b[++cnt]=E[v];}sort(b+1,b+cnt+1);double gu=calc();if(gu>E[u])l=E[u];else r=E[u];}E[u]=l;//printf("%d %.3lf\n",u,E[u]);return;
}int tot;
void add(int a,int b,double c){nxt[++tot]=first[a];first[a]=tot;to[tot]=b;w[tot]=c;
}
signed main(){n=in;m=in;S=in;T=in;for(int i=1;i<=m;i++){int a=in;int b=in;double c;scanf("%lf",&c);add(a,b,c);}dfs(S);if(!toT[S])cout<<"-1";else{printf("%.7lf",E[S]);} return 0;
}

NOI模拟20191029【线段树】【二分+哈希】【迭代+二分】相关推荐

  1. Caesar Cipher(线段树维护哈希)

    题目链接 解题思路: 1.和普通线段树维护哈希不同的是,这里要求原数组对一个数模mod,观察发现区间一次只加1,线段树记录区间最大值,在查询时如果区间的最大值大于等于mod,重建这部分线段树. 2.其 ...

  2. BZOJ 2124 等差子序列 线段树维护哈希

    $ \Rightarrow $ 戳我进BZOJ原题 等差子序列 Time Limit: 3 Sec $ \quad $ Memory Limit: 259 MB Description 给一个 $ 1 ...

  3. NOIP模拟题 [线段树][矩阵快速幂]

    有一定难度,要深入挖掘问题特性. T1: 题意: 给定一个序列,每次操作把操作位置及其后面比它小的数按顺序排列(整体上仍在原来的位置),求每次操作后的逆序对数. 分析: 每个数对逆序对数都有一个贡献( ...

  4. jsk Star War (线段树维护区间最小最大值 + 二分)

    Description 公元20XX年,人类与外星人之间的大战终于爆发. 现有一个人类军团,由n名士兵组成,第i个士兵的战斗力值对应一个非负整数ai (1 \leq i \leq n1≤i≤n). 有 ...

  5. l2-004 这是二叉搜索树吗? (25分)_什么是 “线段树” ?

    线段树是一个复杂的数据结构,比较难理解,也比较难解释清楚.在我将这个数据结构反复学习了五遍的时候,我终于有了信心写出这篇介绍线段树的文章.希望大家能够掌握这种数据结构. 这篇文章比较长,建议大家耐心阅 ...

  6. 小学六年级学生写的 “线段树”解析,厉害了!

    作者 | 王乙堃 来源 | 程序员小灰 线段树是一个复杂的数据结构,比较难理解,也比较难解释清楚.在我将这个数据结构反复学习了五遍的时候,我终于有了信心写出这篇介绍线段树的文章.希望大家能够掌握这种数 ...

  7. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  8. 【算法微解读】浅谈线段树

    浅谈线段树 (来自TRTTG大佬的供图) 线段树个人理解和运用时,认为这个是一个比较实用的优化算法. 这个东西和区间树有点相似,是一棵二叉搜索树,也就是查找节点和节点所带值的一种算法. 使用线段树可以 ...

  9. CodeForces - 731D 80-th Level Archeology(线段树+暴力/差分)

    题目链接:点击查看 题目大意:给出 n 个数列,再给出一个模数 mod,每次操作可以将所有的数字进行:x = x %mod + 1 操作,问至少进行多少次操作,才能使得 n 个数列按照字典序非降序排列 ...

最新文章

  1. 【leetcode】Best Time to Buy and Sell Stock
  2. 微软开源的Web测试和自动化神器 Playwright
  3. 在.Net Core WebAPI下给Swagger增加导出离线文档功能
  4. es6 Proxy.revocable()方法
  5. 2019手机号码正则表达式
  6. 安徽计算机对口大学有哪些专业,计算机专业对口升学安徽院校
  7. python s append_Python Pandas Series.append()用法及代码示例
  8. jmake 编译当前目录所有c/c++单文件
  9. Cesium:结合天地图实现中文定位
  10. 安卓问题-第三方相关
  11. 【工具分享】一个阿里出品的免费在线图表制作工具(ChartCube 图表魔方)
  12. sci四区计算机期刊,计算机 | SCI期刊专刊信息4条
  13. 基于粒子群优化算法的微型燃气轮机冷热电联供系统优化调度(Matlab代码实现)
  14. 算法 - 随机密码生成算法
  15. SAP中采购订单修改触发重新审批的相关控制逻辑及测试
  16. matlab 曲线填充 透明度
  17. windows 10 ISO 纯净版 官方来源
  18. 带有三角函数的计算机,三角函数计算器
  19. Google地图的开发让我很有挫败感
  20. 云服务器上传文件软件,云服务器上传文件软件

热门文章

  1. ubuntu20.04安装思维导图软件MindMaster、XMind
  2. 【HA】HomeAssistant 添加 小米温湿度计2代
  3. 如何通过Google搜索PDF、PPT、DOc等类型的学术文献?(转载)
  4. python画熊猫代码_Python 绘制散点图(Pandas + Matplotlib)
  5. 人为什么觉得心累呢,很值得看!
  6. FPGA零基础学习:IP CORE 之 FIFO设计
  7. PyQt5 实现滚动字幕效果且字幕可以更新 可直接简单套用(甚至无需理解)
  8. 一个好用的倒数日小程序:「那天鸭」
  9. 华为鸿蒙系统含义,华为鸿蒙系统正在的意义
  10. js使用canvas实现视频截图