题解-CF1418G Three Occurrences
题面
给一个 \(n\) 个数的序列 \(a_i\),求每个出现过的数出现次数为 \(3\) 的子序列个数。
数据范围:\(1\le n\le 5\cdot 10^5\),\(1\le a_i\le n\)。
蒟蒻语
做了半个上午做出了最离谱的做法:表格打叉叉。
用线段树求矩阵面积并实现,时间复杂度 \(\Theta(n\log n)\),总用时 \(1.40 min\)。
蒟蒻解
想象有一个 \(n\times n\) 的表格,行表示右端点,列表示左端点。
如果表格上的数为 \(1\) 表示这个区间没被叉掉,对答案有贡献,否则这个区间不符合题目要求。
可以发现每次叉的都是一个矩阵,线段树扫描线求矩阵面积并即可。
考虑数据:
8
1 1 2 1 2 1 2 1
answer=2
现在的表表是:
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
\((1)\) 把左端点大于右端点的给叉了:
| \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
\((2)\) 把超过 \(3\) 个相同的数的区间叉了:
| \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) |
| \(0\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(0\) |
| \(0\) | \(0\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) | \(1\) |
\((3)\) 把少于 \(3\) 个相同的数的区间叉了:
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| \(0\) | \(0\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
然后剩下的 \(2\) 就是答案了。
手玩一下就可以发现叉的规律了,非常简单易懂,蒟蒻就不负责任地放代码里了。
代码
#include <bits/stdc++.h>
using namespace std;
//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair((a),(b))
#define x first
#define y second
#define be(a) (a).begin()
#define en(a) (a).end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i,a,b) for(int i=(a),I=(b);i<I;i++)
#define L(i,a,b) for(int i=(b)-1,I=(a)-1;i>I;i--)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;
//Data
const int N=5e5;
int n,a[N],d[N]; ll ans;
vector<int> id[N];
struct line{
int l,r,t; line(){}
line(int l,int r,int t):l(l),r(r),t(t){}
};
vector<line> p[N+1];
void addmat(int xl,int xr,int yl,int yr){
// cout<<"(["<<xl<<","<<xr<<"),["<<yl<<","<<yr<<"))\n";
p[xl].pb(line(yl,yr,1)),p[xr].pb(line(yl,yr,-1));
}
//SegmentTree
const int tN=N<<2;
#define mid ((l+r)>>1)
int mn[tN],mc[tN],mk[tN];
void pushup(int k){
if(mn[k*2+1]<mn[k*2+2]) mn[k]=mn[k*2+1],mc[k]=mc[k*2+1];
else if(mn[k*2+1]>mn[k*2+2]) mn[k]=mn[k*2+2],mc[k]=mc[k*2+2];
else mn[k]=mn[k*2+1],mc[k]=mc[k*2+1]+mc[k*2+2];
}
void pushadd(int k,int v){mn[k]+=v,mk[k]+=v;}
void pushdown(int k){pushadd(k*2+1,mk[k]),pushadd(k*2+2,mk[k]),mk[k]=0;}
void build(int k=0,int l=0,int r=n){
if(r-l==1) return mn[k]=0,mc[k]=1,void();
build(k*2+1,l,mid),build(k*2+2,mid,r),pushup(k);
}
void add(int x,int y,int v,int k=0,int l=0,int r=n){
if(r<=x||y<=l) return; if(x<=l&&r<=y) return pushadd(k,v);
pushdown(k),add(x,y,v,k*2+1,l,mid),add(x,y,v,k*2+2,mid,r),pushup(k);
}
//Main
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
R(i,0,n) id[i].pb(-1),id[i].pb(-1),id[i].pb(-1);
R(i,0,n) cin>>a[i],--a[i],d[i]=sz(id[a[i]]),id[a[i]].pb(i);
R(i,0,n) id[i].pb(n),id[i].pb(n),id[i].pb(n);
// cout<<"ok 60\n";
R(i,0,n){
addmat(i,i+1,i+1,n); // 把左端点大于右端点的给叉了
addmat(i,n,0,id[a[i]][d[i]-3]+1); //把超过 3 个相同的数的区间叉了
addmat(i,id[a[i]][d[i]+2],id[a[i]][d[i]-1]+1,i+1); //把少于 3 个相同的数的区间叉了
}
// cout<<"ok 66\n";
build();
R(i,0,n){ //求个矩阵面积并
for(line u:p[i])if(u.r>u.l) add(u.l,u.r,u.t);
ans+=mc[0]*(mn[0]==0);
}
cout<<ans<<'\n';
return 0;
}
祝大家学习愉快!
题解-CF1418G Three Occurrences的更多相关文章
- CF1418G Three Occurrences
统计满足某些性质的区间个数. 我们考虑移动 \(r\) 指针. 然后考虑把不能选的区间 \(ban\)掉. 具体看下细节吧. #include<iostream> #include< ...
- 【字符串】【hash】【倍增】洛谷 P3502 [POI2010]CHO-Hamsters 题解
这是一道字符串建模+图论的问题. 题目描述 Byteasar breeds hamsters. Each hamster has a unique name, consisting of lo ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
随机推荐
- matlab 第五章单元数组、字符串作业
1.创建 2×2 单元数组,第 1.2 个元素为字符串,第三个元素为整型变量,第四个元素为双精度(double)类型,并将其用图形表示. A=cell(2,2); A(1,1)={'mat'}; A( ...
- [LeetCode题解]23. 合并K个升序链表 | 分治 + 递归
方法一:分治 + 递归 解题思路 在21. 合并两个有序链表,我们知道如何合并两个有序链表.而本题是合并 k 个有序链表,可以通过大问题拆分成小问题解决,即把 k 个链表,拆分成 k/2 个链表组,俩 ...
- 金九银十已到!Cookie 和 Session的这些知识你必须知道,面试必问!
前言 会话:一次会话中包含多次请求和响应 注:一次会话表示浏览器第一次给服务器发送请求,会话建立,直到有一方断开为止 功能:在一次会话的多次请求间共享数据 方式: (1) 客户端会话技术:Cookie ...
- 公司新来的小姐姐不懂java中的static关键字,这样给她描述不香吗?
前言 static关键字是摆在刚入行编程语言的小白们面前的一道难题,为什么要用static?使用它有什么好处?修饰方法和修饰变量有什么区别?本文将就java中static关键字的使用方法及注意事项进行 ...
- 如何使用OCR编辑器检查和识别文本
ABBYY FineReader 15(Windows系统)中的OCR编辑器能帮助用户对扫描仪或者数码相机获取的图像文件进行自动文本识别,OCR区域绘制等,使这些图像文件能进一步转换为可编辑的格式.其 ...
- 「CSP-S 2019」Emiya 家今天的饭
description loj 3211 solution 看到题目中要求每种主要食材至多在一半的菜中被使用,容易想到补集转换. 即\(ans=\)总方案数-存在某一种食材在一半以上的菜中被使用的方案 ...
- Jsoup获取网页内容(并且解决中文乱码问题)
1. 根据连接地址获取网页内容,解决中文乱码页面内容,请求失败后尝试3次 private static Document getPageContent(String urlStr) { for (in ...
- YoyoGo微服务框架入门系列-基本概念
前言 Github开源:github.com/yoyofx/yoyogo 还请多多Star 之前简单介绍了YoyoGo微服务框架的基本内容,接下来了解下框架中的基本概念. 从一个简单Web服务Demo ...
- Fiddler 4 对app接口抓取
一.先打开模拟器 二.在Fiddler 4 选项中修改端口号和去掉一个勾选 三.在终端查看ip 输入ipconfig 四.点开模拟器的设置 五.点击WLAN 六.长按网络,修改网络 七.输入ip端口号 ...
- 查询Oracle日志文件的方法
Oracle日志文件相信经常使用Oracle数据库的朋友都比较熟悉了,下面将为您介绍的是查询Oracle日志文件的几种方法,供您参考学习. 1.查询系统使用的是哪一组日志文件: select * fr ...