20190716-T3-奇袭
我要嗝了
我经过一系列努力,寻找了一系列,各种复杂度的方法。
1>纯暴力
复杂度:$\Theta(N^5)$
不多解释,上代码:
空间复杂度无法承受,如果考试偏要写这个不妨动态开数组:
例:
#include<iosteam>
using namespace std;
int n;
int *Array;//开一个指针
int main(){
cin>>n;
Array=new int[n];//像这样
}
全码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define N 5000
using namespace std;
int le,ans;
bool ma[N][N];
int main(){
//freopen("raid.in","r",stdin);
//freopen("raid.out","w",stdout);
int a,b;
scanf("%d",&le);
for (int i=1;i<=le;i++)
scanf("%d%d",&a,&b),ma[a][b]=1;
for (int k=0;k<le;k++){
for (int i=1;i<=le;i++){
for (int j=1;j<=le;j++){
int cnt=0;
for (int x=1;x<=le;x++){
for (int y=1;y<=le;y++){
if(ma[x][y])cnt++;
}
}
if(cnt==k+1)ans++;
}
}
}
cout<<ans<<endl;
return 0;
}
2>一般人想的到的暴力
复杂度:$\Theta(N^4)$
枚举长度$\Theta(N)$,开头$\Theta(N^2)$,再判断$\Theta(N)$。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define N 50001
using namespace std;
int le,x[N],y[N],ans;
int main(){
//freopen("raid.in","r",stdin);
//freopen("raid.out","w",stdout);
int a,b;
scanf("%d",&le);
for (int i=1;i<=le;i++)
scanf("%d%d",&x[i],&y[i]);
for (int k=0;k<le;k++){
for (int i=1;i<=le;i++){
for (int j=1;j<=le;j++){
int num=0;
for (int t=1;t<=le;t++){
if(i<=x[t]&&j<=y[t]&&x[t]<=i+k&&y[t]<=j+k){
num++;
}
}
if(num==k+1)ans++;
}
}
}
cout<<ans<<endl;
return 0;
}
3>一棵二维线段树(我疯了)
复杂度:$\Theta(N^3 \log_4 N+N\log_4N)$
常数巨大,T到飞起……9分好成绩(我猜有的点输入时就直接T飞)
插入一个点后这个树就变成这样(每一个矩形,包括已经被切开的点都是一个节点)
一个节点有四个儿子(左上,左下,右上,右下),如果不动态开点原地MLE,如果动态开点T飞
UPD:
这种写法操作容易被卡成$\Theta(N)$,有另一种写法可以保证$\Theta(\log^2 N)$的复杂度。
其实就是在线段树的叶节点上再开线段树。
请大家自行百度:<链接>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
//#include "debug.h"
#define N 50001
using namespace std;
int le;
struct XDS{
int dat;
XDS* rd,*ru,*ld,*lu;
XDS(){
dat=0;
rd=ru=lu=ld=NULL;
}
}*root;
void add(XDS *&rt,int x,int y,int xl,int xr,int yl,int yr){
if(rt==NULL)rt=new XDS();
if(xl==xr&&yl==yr){
rt->dat++;
return ;
}
int xmid=(xl+xr)>>1,ymid=(yl+yr)>>1;
if (x>xmid &&y>ymid ) add(rt->rd,x,y,xmid+1,xr ,ymid+1,yr);
else if(x>xmid &&y<=ymid) add(rt->ru,x,y,xmid+1,xr ,yl ,ymid);
else if(x<=xmid&&y>ymid ) add(rt->ld,x,y,xl ,xmid,ymid+1,yr);
else add(rt->lu,x,y,xl ,xmid,yl ,ymid);
int sum=0;
if(rt->rd!=NULL) sum+=rt->rd->dat;
if(rt->ru!=NULL) sum+=rt->ru->dat;
if(rt->ld!=NULL) sum+=rt->ld->dat;
if(rt->lu!=NULL) sum+=rt->lu->dat;
rt->dat=sum;
}
int ask(XDS *rt,int axl,int axr,int ayl,int ayr,int xl,int xr,int yl,int yr){
if(rt==NULL)return 0;
if(axl<=xl&&axr>=xr&&ayl<=yl&&ayr>=yr) return rt->dat;
int none=0;
int xmid=(xl+xr)>>1,ymid=(yl+yr)>>1;
none+=ask(rt->rd,axl,axr,ayl,ayr,xmid+1,xr ,ymid+1,yr);
none+=ask(rt->ru,axl,axr,ayl,ayr,xmid+1,xr ,yl ,ymid);
none+=ask(rt->ld,axl,axr,ayl,ayr,xl ,xmid,ymid+1,yr);
none+=ask(rt->lu,axl,axr,ayl,ayr,xl ,xmid,yl ,ymid);
return none;
}
int ans=0;
int main(){
int a,b;
scanf("%d",&le);
for (int i=1;i<=le;i++){
scanf("%d%d",&a,&b);
add(root,a,b,1,le,1,le);
}
for (int k=1;k<=le;k++){
for (int i=1;i<=le;i++){
if(i+k-1>le)continue;
for (int j=1;j<=le;j++){
if(j+k-1>le)continue;
if(ask(root,i,i+k-1,j,j+k-1,1,le,1,le)==k)ans++;
}
}
}
cout<<ans<<endl;
return 0;
}
4>二维前缀和
复杂度:$\Theta(N^3)$
空间惊人,开不了那么大,考试想到了就可着部分分去吧
#include <iostream>
#include <cstdio>
#include <cstring> //#include "debug.h" #define N 1000 using namespace std; int n;
bool x[N][N];
int pre[N][N];
int ans=0;
int main(){
int a,b;
cin>>n;
for (int i=1;i<=n;i++){
scanf("%d%d",&a,&b);
x[a][b]=1;
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
pre[i][j]=x[i][j]+pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1];
}
}
for (int k=1;k<=n;k++){
for (int i=1;i<=n;i++){
if(i+k-1>n)continue;
for (int j=1;j<=n;j++){
if(j+k-1>n)continue;
cout<<i<<" "<<j<<endl;
if(pre[i+k-1][j+k-1]-pre[i-1][j+k-1]-pre[i+k-1][j-1]+pre[i-1][j-1]==k)ans++;
}
}
}
//pour(pre,1,n,1,n,3,"pre");
cout<<ans<<endl;
return 0;
}
5>线段树优化一拨
我们在这里发现了这个题的真实题面:给定$N$个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。
进一步可以化为:有多少种情况使得,相邻的$k$个数中最大值和最小值的差小于等于$k-1$。
复杂度:$\Theta(N^2 \log N)$
还不错,没试能得几分
#include <iostream>
#include <cstdio>
#include <cstring>
//#include "debug.h"
#define N 50500
using namespace std;
int x[N],n;
struct XDS{
int maxn,minn;
}tr[4*N];
void build(int id,int l,int r){
if(l==r){
tr[id].maxn=tr[id].minn=x[l];
return ;
}
int mid=(l+r)>>1;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
tr[id].maxn=max(tr[id*2].maxn,tr[id*2+1].maxn);
tr[id].minn=min(tr[id*2].minn,tr[id*2+1].minn);
}
int askmax(int id,int l,int r,int al,int ar){
if(l>=al&&r<=ar)return tr[id].maxn;
int val,mid=(l+r)>>1;
val=0;
if(mid>=al)
val=max(val,askmax(id*2 ,l ,mid,al,ar));
if(mid<ar)
val=max(val,askmax(id*2+1,mid+1,r ,al,ar));
return val;
}
int askmin(int id,int l,int r,int al,int ar){
if(l>=al&&r<=ar)return tr[id].minn;
int val,mid=(l+r)>>1;
val=0x7fffffff;
if(mid>=al)
val=min(val,askmin(id*2 ,l ,mid,al,ar));
if(mid<ar)
val=min(val,askmin(id*2+1,mid+1,r ,al,ar));
return val;
}
int ans=0;
int main(){
int a,b;
cin>>n;
for (int i=1;i<=n;i++){
scanf("%d%d",&a,&b);
x[a]=b;
}
build(1,1,n);
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
if(i<=j){
if(askmax(1,1,n,i,j)-askmin(1,1,n,i,j)==j-i)ans++;
}
else{
if(askmax(1,1,n,j,i)-askmin(1,1,n,j,i)==j-i)ans++;
}
//puts("DDD");
}
}
cout<<ans<<endl;
return 0;
}
6>(借的)别人的暴力(?)
复杂度:$\Theta(N^2)$
很优秀了,蒟蒻OTZ
#include<cstdio>
#include<ctime>
using namespace std;
#define rus register unsigned short
inline unsigned short max(const rus a,const rus b){return a>b?a:b;}
inline unsigned short min(const rus a,const rus b){return a<b?a:b;}
inline unsigned short read(){
rus a=0;register char ch=getchar();
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57)a=(a<<3)+(a<<1)+ch-48,ch=getchar();
return a;
}
int ans;
unsigned short pos[50005],n;
int main(){
n=read();
for(rus i=1,x,y;i<=n;++i) x=read(),y=read(), pos[x]=y;
for(rus i=1,maxx=0,minn=50006;i<=n;++i,maxx=0,minn=50006){
if(clock()>990000){printf("%d\n",ans+n-i+1);return 0;}
for(rus j=i;j<=n;++j){
maxx=max(maxx,pos[j]); minn=min(minn,pos[j]);
if(maxx-minn==j-i)ans++;
}
} printf("%d\n",ans);//printf("%ld\n",clock());
} //考场上DeepinC的n2卡常,应该能看出来
7>终于到了正解%%%
复杂度:使用 reverse(); $\Theta(N\log^2N)$
不用:$\Theta(N\log N)$
这个正解我好像解释不清楚~~
我尽量
首先我们可以想到分治,
把区间分成两部分,当前的区间答案就可以表示为$ans_{[l,mid]}+ans_{[mid+1,r]}+$跨区间方案
你很好想前两部分如何写(递归)
问题就出在跨区间这里
有两种,
最值在一边:
我纳过闷,最值在一边,怎么跑另一边????
但是:可以这样:
O | X | X | X |
X | X | O | X |
X | X | X | O |
X | O | X | X |
表示成一维:$\{1,4,2,3\}$
发现可以延伸到那边
我怎么算?
要用这个条件:$\begin{array}{cc}\max\{A_i \cdots A_{mid}\} & > & \max\{A_{mid+1} \cdots A_j \} \\ \min\{A_i \cdots A_{mid}\} & < & \min\{A_{mid+1} \cdots A_j\}\end{array}$其中$i-j=\max-\min$
最值在两边:单调栈思想?桶
解释不清楚~
上关键代码:
/*下面是判最大最小在左右的*/
int ll=mid+1,rr=mid+1;//两个指针记录可行区间,每次我们去搜小的
for(int k=mid;k>=l;k--)//左小右大
{
while(minn[rr]>minn[k]&&rr<=r) check(maxn[rr]-rr)++,rr++;//把一段合法区间都标记一下
while(maxn[ll]<maxn[k]&&ll<rr) check(maxn[ll]-ll)--,ll++;//把非法的情况剪掉
ans+=check(minn[k]-k);
}
while(ll<rr) check(maxn[ll]-ll)--,ll++;//好像????是恢复初始状态
ll=mid,rr=mid;
for(int k=mid+1;k<=r;k++)//左大右小
{
while(minn[rr]>minn[k]&&rr>=l) check(maxn[rr]+rr)++,rr--;//同上
while(maxn[ll]<maxn[k]&&ll>rr ) check(maxn[ll]+ll)--,ll--;
ans+=check(minn[k]+k);
}
while(ll>rr) check(maxn[ll]+ll)--,ll--;
return ans;
终于让我水了它……
#include <iostream>
#include <cstdio>
#define N 50505
#define check(i) tong[(i)+50000]
#define Inf 0x7fffffff
using namespace std;
int n,arr[N],Ans=0;
int maxn[N],minn[N];
int tong[3*N];
int divide(int l,int r){//分治
if(l==r)return 1;//递归边界
int ans=0,mid=(l+r)>>1;//二分
ans+=divide(l,mid);//左右的分区间
ans+=divide(mid+1,r);
maxn[mid]=minn[mid]=arr[mid];//处理i到mid之间的最大最小值
maxn[mid+1]=minn[mid+1]=arr[mid+1];
for (int i=mid+2;i<=r;i++){ //处理
maxn[i]=max(arr[i],maxn[i-1]);
minn[i]=min(arr[i],minn[i-1]);
}
for (int i=mid-1;i>=l;i--){
maxn[i]=max(arr[i],maxn[i+1]);
minn[i]=min(arr[i],minn[i+1]);
}
for (int i=mid;i>=l;i--){ //处理中间到左边的最值
int j=maxn[i]-minn[i]+i; //利用maxn-minn=j-i推的j
if(/*1*/j>mid&& //合法区间
/*2*/j<=r&&
/*3*/maxn[j]<maxn[i]&& //满足maxn-minn
/*4*/minn[j]>minn[i]){
ans++;
}
}
for (int i=mid+1;i<=r;i++){
int j=i-maxn[i]+minn[i]; //推的第二个 maxn-minn=i-j
if(/*1*/j>=l&&
/*2*/j<=mid&&
/*3*/maxn[j]<maxn[i]&& //要满足maxn和minn
/*4*/minn[j]>minn[i]){
ans++;
}
}
/*下面是判最大最小在左右的*/
int ll=mid+1,rr=mid+1;//两个指针记录可行区间,每次我们去搜小的
for(int k=mid;k>=l;k--)//左小右大
{
while(minn[rr]>minn[k]&&rr<=r) check(maxn[rr]-rr)++,rr++;//把一段合法区间都标记一下
while(maxn[ll]<maxn[k]&&ll<rr) check(maxn[ll]-ll)--,ll++;//把非法的情况剪掉
ans+=check(minn[k]-k);
}
while(ll<rr) check(maxn[ll]-ll)--,ll++;//好像????是恢复初始状态
ll=mid,rr=mid;
for(int k=mid+1;k<=r;k++)//左大右小
{
while(minn[rr]>minn[k]&&rr>=l) check(maxn[rr]+rr)++,rr--;//同上
while(maxn[ll]<maxn[k]&&ll>rr ) check(maxn[ll]+ll)--,ll--;
ans+=check(minn[k]+k);
}
while(ll>rr) check(maxn[ll]+ll)--,ll--;
return ans;
}
int main(){
int a,b;
scanf("%d",&n);
for (int i=0;i<n;i++){
scanf("%d%d",&a,&b);
arr[a]=b;
}
printf("%d\n",divide(1,n));
return 0;
}
20190716-T3-奇袭的更多相关文章
- 模拟4题解 T3奇袭
T3奇袭 题目描述 由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上 要迎来最终的压力测试——魔界入侵. 唯一一个神一般存在的Administrator被消灭了,靠原本 ...
- 7.16 NOIP模拟测试4 礼物+通讯+奇袭
T1 礼物 题目大意:n个物品,每次有pi的概率买到,可以重复买,也可以什么都没买到,但算一次购买,问把所有东西都买到的期望次数.对于10%的数据,N = 1;对于30%的数据,N ≤ 5;对于100 ...
- 奇袭(单调栈+分治+桶排)(20190716 NOIP模拟测试4)
C. 奇袭 题目类型:传统 评测方式:文本比较 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目描述 由于各种原因,桐人现在被困在Under World(以下简称UW)中,而 ...
- [NOIP2016]愤怒的小鸟 D2 T3 状压DP
[NOIP2016]愤怒的小鸟 D2 T3 Description Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可 ...
- [NOIP2016]换教室 D1 T3 Floyed+期望DP
[NOIP2016]换教室 D1 T3 Description 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 ...
- T3 - 构建大型 Web 应用的 JavaScript 框架
T3 是一个用于构建大型 Web 应用程序的客户端 JavaScript 框架.T3 和大多数的 JavaScript 框架不同.它的意思是一小部分的整体架构,它允许你建立可扩展的客户端代码.T3 应 ...
- [NOIP2015]运输计划 D2 T3 LCA+二分答案+差分数组
[NOIP2015]运输计划 D2 T3 Description 公元2044年,人类进入了宇宙纪元. L国有n个星球,还有n-1条双向航道,每条航道建立在两个星球之间,这n-1条航道连通了L国的所有 ...
- 有三个线程T1 T2 T3,如何保证他们按顺序执行-转载
T3先执行,在T3的run中,调用t2.join,让t2执行完成后再执行t3 在T2的run中,调用t1.join,让t1执行完成后再让T2执行 public class Test { // 1.现在 ...
- 现在有T1、T2、T3三个线程,怎样保证T2在T1执行完后执行,T3在T2执行完后执行?使用Join
public class TestJoin { public static void main(String[] args) { Thread t1 = new Thread(new T1(), &q ...
- 学军NOI训练13 T3 白黑树
唉,大学军有自己的OJ就是好,无限orz 只有周六的比赛是开放的囧,这场比赛最后因为虚拟机卡住没有及时提交…… 否则就能让大家看到我有多弱了…… 前两题题解写的很详细,可以自己去看,我来随便扯扯T3好 ...
随机推荐
- 记录:使用springboot的cors和vue的axios进行跨域
一.编写一个配置类,并且注册CorsFilter: 注意允许跨域的域名不要写错 @Configuration public class ZysuyuanCorsConfiguration { @Bea ...
- JavaScript特效源码(4、鼠标特效)
1.鼠标感应--渐现特效 鼠标感应渐显图片[平时很模糊的图片鼠标一放上就显示清楚] [修改图片名称即可][共2步] ====1.将以下代码加入HTML的<head></head> ...
- Android 开发 Camera1_如何使用对焦功能
前言 Camera1的自动对焦还是有一些坑值得开一个篇幅来讲解,一般对焦Mode有以下几种: Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO 连续自动对焦视 ...
- 【BZOJ3944】Sum
题面 Description Input 一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问 Output 一共T行,每行两个用空格分隔的数ans1, ...
- 在github上怎样克隆vue项目及运行
长时间不做vue项目,今天看vue项目运行时有些指令忘记了,在这里写下相关指令 .克隆已有项目,一般情况项目中的README.md写的是项目运行步骤,一般项目的运行如下 克隆项目 git clone ...
- CSS3中2D/3D转换、过渡、动画
转换.过渡.动画 2D 转换 1.translate() 方法 通过 translate() 方法,元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数: 实例 d ...
- PAT甲级——A1099 Build A Binary Search Tree
A Binary Search Tree (BST) is recursively defined as a binary tree which has the following propertie ...
- C#计算两个时间的时间差,精确到年月日时分秒
喏,计算两个时间的时间差,精确到年月日时分秒 看起来比较笨的方法了,不知道有没有改进 DateTime d1 = new DateTime(2016, 4, 1, 0, 0, 0); DateTime ...
- Ubuntu下U盘能看见盘符但打不开
查看U盘状态 sudo fdisk -l 格式化 sudo mkfs -t vfat -I /dev/sdb1 sudo mkfs -t ntfs -I /dev/sdb1 sudo mkfs -t ...
- 操作系统 Lab1(2)
中断很久,一看发现又多了一些内容. 打算完成了 Lab1 challenge 1 中断像量表设置的时候我们需要设置一个用于系统调用的 trap门 也就是 利用中断切换特权级 To kernel 调用 ...