寒武纪camp Day2
补题进度:8/10
A(计数+BIT)
题意:
给一个长度为n的数组a[],任意选0<=i<=j<n,将a[i]~a[j]从小到大排序,形成新的数组。问有多少个不同的新数组。
N,a[i]<=1000000
分析:
对答案有贡献的ij一定是a[i]不是i~j的最小值,a[j]不是i~j的最大值,于是我们就去统计这样区间的数量
我们用单调栈搞出r[i]表示i右边第一个比a[i]小的位置,l[i]表示i左边第一个比a[i]大的位置
枚举区间左端点,那么可行的右区间在[r[i],n]中,然后我们只需要统计其中有多少位置的l[j]>=i就行了
这个可以离线+BIT解决
B(并查集维护基环外向树森林)
题意:
一个长度为n的01数组,最初都是0,有m个颜色,每个位置有两种染色选择,每种颜色只可以染一次。现在你需要找出一个染色方案满足:
1、被染色的位置数量尽可能多
2、在1的前提下,若用1表示该位置被染,用2表示该位置没被染,你需要让这个字典序尽量大
3、在2的前提下,一个位置如果被染那么它要么是1要么是2(表示染了第一种备选颜色还是第二种),你需要让这个字典序尽量小
n,m<=5*10^5
分析:
如果是问最多染色的个数,那显然只需要维护一下基环外向树森林就行了
现在要输出方案,我们发现第2个条件其实就是让我们尽可能优先用先读入的边,这个在我们维护基环外向树森林的时候就成功处理了
第三个条件其实是让我们考虑最后的基环树/树的染色分配
首先对于基环树,只有环上的有两种选择,树边是定死的,我们只需要枚举两种情况即可
对于树,我们贪心来看,取出id最小的边,令其为1,按照方向去dfs涂色,再取出id次小的边,以此类推……
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6,inf=1e7;
int ans[maxn+],l[maxn+],r[maxn+];
int f[maxn+];
bool c[maxn+],incircle[maxn+];
bool vis[maxn+];
int n,m,len,top;
vector<int> circle,E;
struct edge
{
int from,to,id;
}e[maxn*+];
int head[maxn+],nx[maxn+];
int s[maxn+];
bool use[maxn*+];
void addedge(int u,int v,int id)
{
++len;
e[len]={u,v,id};
nx[len]=head[u];
head[u]=len;
++len;
e[len]={v,u,id};
nx[len]=head[v];
head[v]=len;
}
int find(int x)
{
if(f[x]==x) return x;else return f[x]=find(f[x]);
}
void dfs(int k)
{
if(vis[k])
{
int now=top;
circle.push_back(s[now]);
--now;
while(now>=&&e[s[now]].to!=k) circle.push_back(s[now--]);
return;
}
vis[k]=;
for(int i=head[k];i!=-;i=nx[i])
{
if(use[i]) continue;
edge u=e[i];
s[++top]=i;
use[i]=use[i^]=;
dfs(u.to);
--top;
}
}
void paint(int k,int fa)
{
for(int i=head[k];i!=-;i=nx[i])
{
edge u=e[i];
if(u.to==fa||incircle[u.to]) continue;
if(k==l[u.id]) ans[u.id]=;else ans[u.id]=;
paint(u.to,k);
}
}
void workcircle()
{
for(int i=;i<circle.size();++i) incircle[e[circle[i]].to]=;
for(int i=;i<circle.size();++i) paint(e[circle[i]].to,);
int minid=n+;
for(int i=;i<circle.size();++i)
{
edge u=e[circle[i]];
if(u.to==l[u.id]) ans[u.id]=;else ans[u.id]=;
minid=min(minid,u.id);
}
if(ans[minid]==)
for(int i=;i<circle.size();++i) ans[e[circle[i]].id]=-ans[e[circle[i]].id];
}
void dfs1(int k,int fa)
{
for(int i=head[k];i!=-;i=nx[i])
{
edge u=e[i];
if(u.to==fa) continue;
E.push_back(i);
dfs1(u.to,k);
}
}
bool cmp(const int x,const int y)
{
return e[x].id<e[y].id;
}
void worktree(int start)
{
E.clear();
dfs1(start,);
sort(E.begin(),E.end(),cmp);
for(int i=;i<E.size();++i)
{
edge u=e[E[i]];
if(ans[u.id]) continue;
ans[u.id]=;
if(u.to==l[u.id]) paint(u.to,u.from);else paint(u.from,u.to);
}
}
int main()
{
int size = << ; // 256MB
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p));
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i) f[i]=i,head[i]=-;
len=-;
for(int i=;i<=n;++i)
{
scanf("%d%d",&l[i],&r[i]);
int u=find(l[i]),v=find(r[i]);
if(c[u]&&c[v]) continue;
if(u!=v) f[u]=v,c[v]|=c[u];
else
if(!c[u]&&!c[v]) f[u]=v,c[v]=;
else continue;
addedge(l[i],r[i],i);
}
for(int i=;i<=m;++i)
if(!vis[i])
{
circle.clear();
top=;
dfs(i);
if(!circle.empty()) workcircle();
else worktree(i);
}
for(int i=;i<=n;++i) printf("%d",ans[i]);
return ;
}
C(计数+线段树)
题意:
给出一个长度为n的数组a[],问有多少对(x,y)满足:
1、0<=x<=y<n
2、任意i∈[x,y] & j∉[x,y],有a[i]≠a[j]
n<=1000000
分析:
直观来讲,我们是要找区间[x,y]使得颜色被分开了
我们把同样的颜色提取出来,那么对于每个位置我们都能方便的求出l[i],r[i]表示a[i]这个颜色最左边在哪,最右边在哪
那么x只能是一个颜色的起点,y只能是一个颜色的终点
我们去枚举y,计算有多少个合法的x
即要满足max{r[x],r[x+1],...,r[y]}<=y 且 min{l[x],l[x+1],...,l[y]}>=x
那么对于每个y,我们可以用线段树求出最左边的x满足max{r[x],r[x+1],...,r[y]}<=y,记为posl[y]
对于每个x,我们可以用线段树求出最右边的y满足min{l[x],l[x+1],...,l[y]}>=x,即为posr[x]
那么我需要找出[posl[y],y]里面有多少少符合题意的x,即那些posr[x]>=y的x,一眼直接用可持久化线段树支持询问
但仔细分析会发现这样的posr[x]是单减的,所以同样只需要用线段树找出那个分界就行了
D(组合计数)
略
E(构造)
题意:
给你一颗n个点的二叉树,你需要给每个点重新编号1~n使得{fa[2],fa[3],fa[4],...,fa[n]}这个序列字典序最小
n<=2e5
分析:
显然是用bfs的顺序给每个点重新编号,但主要矛盾点就是某个点有两个孩子,两个孩子谁先编号的问题
显然贪心选择这两个孩子当中size最大的那个
若这两个孩子的size又相同呢……?
那么再比较这两个孩子的孩子……这样深入下去
这样时间复杂度能保证吗?看似是O(n^2)的啊
每次考察一个点u,向下需要一直深入到size有分歧的时候,考虑最差情况就是完全二叉树
完全二叉树的情况下复杂度是O(nlogn)的,所以这个算法是work的
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
vector<int>g[maxn+];
int ans[maxn+],fa[maxn+];
int n;
bool check(int x,int y)
{
queue<int> p,q;
p.push(x);
q.push(y);
while(true)
{
if(p.empty()) return true;
if(q.empty()) return false;
x=p.front(),p.pop();
y=q.front(),q.pop();
if(g[x].size()!=g[y].size()) return g[x].size()<g[y].size();
for(int i=;i<g[x].size();++i) p.push(g[x][i]);
for(int i=;i<g[y].size();++i) q.push(g[y][i]);
}
return true;
}
void dfs(int k)
{
for(int i=;i<g[k].size();++i) dfs(g[k][i]);
if(g[k].size()==)
if(check(g[k][],g[k][])) swap(g[k][],g[k][]);
}
void paint()
{
queue<int> q;
vector<int> a;
q.push();
int id=;
while(!q.empty())
{
int u=q.front();
//printf("ce : %d\n",u);
q.pop();
++id;
ans[u]=id;
if(u!=) a.push_back(ans[fa[u]]-);
for(int i=;i<g[u].size();++i) q.push(g[u][i]);
}
for(int i=;i<a.size()-;++i) printf("%d ",a[i]);printf("%d",a[a.size()-]);
printf("\n");
}
int main()
{
freopen("ce.in","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=;i<=n;++i) g[i].clear();
for(int i=;i<=n;++i)
{
int x;
scanf("%d",&x);
++x;
g[x].push_back(i);
fa[i]=x;
}
dfs();
//printf("ok\n");
paint();
}
return ;
}
F
待填坑
G(模拟)
题意:
在二维平面上有n个点,你需要开车走折线依次通过这些点,你最初在1号点,问你最少需要转弯多少次才能依次经过这n个点?
n<=1000
分析:
样例比较良心
我们把所有点按照顺序连一下,发现只有出现下面这个情形的时候我们才会做更改
那么我们只需要从前到后去看有多少个这个结构就行了
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3;
struct Point
{
int x,y;
Point()
{ }
Point(int _x,int _y)
{
x=_x,y=_y;
}
Point operator - (const Point &a) const
{
return Point(x-a.x,y-a.y);
}
}p[maxn+];
bool flag[maxn+];
int n,top,ans;
int cross(Point a,Point b)
{
return a.x*b.y-a.y*b.x;
}
bool pall(Point a,Point b)
{
if(cross(a,b)!=) return false;
return a.x*b.x+a.y*b.y>;
}
int main()
{
freopen("ce.in","r",stdin);
//freopen("ce.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y);
for(int i=;i<=n;++i) flag[i]=;
if(n<=)
{
printf("0\n");
continue;
}
ans=;
for(int i=;i<=n;++i)
{
if(!pall(p[i]-p[i-],p[i-]-p[i-])) ++ans;
//printf("%d\n",ans);
if(i!=)
if(!flag[i-]&&!flag[i-]&&1LL*cross(p[i-]-p[i-],p[i]-p[i-])*cross(p[i-]-p[i-],p[i-]-p[i-])>&&cross(p[i]-p[i-],p[i-]-p[i-])!=)
{
if(1LL*cross(p[i-]-p[i-],p[i]-p[i-])*cross(p[i-]-p[i-],p[i-]-p[i-])>)
{ --ans;
flag[i-]=;
}
} }
printf("%d\n",ans);
}
return ;
}
H(状压dp+递归)
题意:
有m道题目,有n个人,用0/1表示每个人是否会做每道题。现在你需要从m道题中挑出8道题组成一场考试,n个人进行这场考试。若一个人至少做出了5题,那么他就及格了。现在问你有多少种组题方案使得最后及格的人数在[l,r]之间并且是3的倍数。
3<=n<=500000 8<=m<=20 3<=l<=r<=n
分析:
枚举每一种组题的方案,计算该组题情况下最后及格的人数
容易想到需要将每个人的解题状态找一个共同的性质把其压缩起来,才能够减少最后的状态数,不然对于每种组题方案都要O(n)遍历
dp[i][s1][j][s2]表示对于前i道题,题目的选择情况是s1,答对了j个题,且这些人后面的答题情况是s2的总人数
很明显s1只有前i位有用,s2只有i后面的有用,于是我们把s1和s2状态合并到一起
那么显然就能状压dp了
分析一下复杂度,是$20*2^{20}*5$的
这是有点超时的,优化常数也不能在1s内通过
研究下原因是题目限定了“选8道题”,所以由很多选题状态都是无用的
于是我们用dfs去代替for循环枚举,就能800ms过了
I(计数+BIT)
题意:
n<=1e6
分析:
仔细分析会发现这个上升序列和下降序列一定会交于唯一位置(枚举这个位置给上升还是下降,会产生两种方案)
那么我们只需要枚举这个交点i,然后统计这个交点是否能对答案产生贡献
能产生贡献那就是:i前面比a[i]小的数字呈上升趋势,比a[i]大的数字呈下降趋势;i后面比a[i]小的数字呈下降趋势,比a[i]大的数字呈上升趋势
那么如何判断呢?以判断i前面比a[i]小的数字是否呈上升趋势为例
我们只需要统计出两个东西,一个东西是从左往右以a[i]为栈顶的单调栈的元素个数,另一个东西是1~i中<=a[i]的数字个数
第二个东西我们只需要用BIT来计算就行了
J
待填坑
寒武纪camp Day2的更多相关文章
- 寒武纪camp网络测试赛
寒武纪camp网络测试赛 地址:oj点我进入 A(树形dp+树链剖分) 题意: 分析: 考虑树形dp,f0(x)和f1(x)分别表示以x为根的子树,不取x点或取x点的最大合法子集的元素个数 那么对于一 ...
- 2020 CCPC-Wannafly Winter Camp Day2
2020 CCPC-Wannafly Winter Camp Day2 A 托米的字符串 虽然每个子串出现的概率是相同的,但是同一长度的子串个数是不同的,所以要分别处理.计算出某一长度的情况下,元音字 ...
- CCPC-Wannafly Winter Camp Day2 (Div2, onsite)
Class $A_i = a \cdot i \% n$ 有 $A_i = k \cdot gcd(a, n)$ 证明: $A_0 = 0, A_x = x \cdot a - y \cdot n$ ...
- 2019 CCPC-Wannafly Winter Camp Day2(Div2, onsite)
solve 4/11 A Erase Numbers II Code:KK Thinking :KK 用ans表示当前最优答案,maxx表示遍历到的最大数字,一开始ans肯定等于a[ 1 ]+a[ 2 ...
- 寒武纪camp Day6
补题进度:10/10 A(树形dp) 略 B(dp) 题意: 给出一个n个关键节点的机械手臂,最开始是竖直的,即关键点在二维平面上的坐标分别是(0,0) (0,100) (0,200) (0,300) ...
- 寒武纪camp Day5
补题进度:6/10 A(状压dp) 题意: 有n个数字1,2,...,n,有m个限制(a,b),表示至少要有一个数字a排在数字b的前面 你需要构造出一个含有数字1~n的序列,数字可以重复多次,要求该序 ...
- 寒武纪camp Day4
补题进度:7/11 A(博弈论) 略 B 待填坑 C(贪心) 题意: 一个序列是good的当且仅当相邻两个数字不相同.给出一个长度为n的数列,每个数字是ai.定义一种操作就是把a中某个元素拿到首位去, ...
- 寒武纪camp Day3
补题进度:9/10 A(多项式) 题意: 在一个长度为n=262144的环上,一个人站在0点上,每一秒钟有$\frac{1}{2}$的概率待在原地不动,有$\frac{1}{4}$的概率向前走一步,有 ...
- 寒武纪camp Day1
补题进度:8/10 A(组合计数) 题意: 一个人站在数轴原点,每秒有1/4概率向前走一步,1/4概率向后走一步,1/2概率不动,问t秒后在p位置的概率. t,p<=100000 分析: 枚举不 ...
随机推荐
- 10个顶级的CSS3代码生成器
新出来的在线工具和web应用允许开发人员快速创建网站,而无需手动一行一行地编写代码.当前,不断有新的框架和代码库涌现在前端开发这个领域里. 但是,这也让许多开发人员忘记了代码生成器以及它们在构建网站时 ...
- 使用windows的fsutil命令创建指定大小及类型的测试文件
在软件测试中,对于上传.下载一类功能常常需要用不同大小的文件进行测试. 使用Windows命令fsutil可以生成任意大小.任意类型文件. C:\Users\axia\fsutil file crea ...
- Django-常用设置(settings.py)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 当前项目的根目录,Django会依此来定位工程内的相关文件 ...
- Python3简明教程(十一)—— 类
本节中将通过定义一些简单的 Python 类,来学习 Python 面向对象编程的基本概念. 定义类 在写你的第一个类之前,你应该知道它的语法.我们以下面这种方式定义类: class nameofth ...
- Linux常用的操作指令
1.pwd-显示当前所在位置 2.cd-进入当前目录 3.cd..-返回上一级目录 4..ls命令参数选项有很多,ls也是经常使用到的命令.如果不清楚命令的使用方式可以直接 ls --help来查看 ...
- CentOS7.4搭建kafka单结点和集群
操作系统选择 CentOS7.4x86-64(操作系统的x86_64是跟CPU有关的,最早AMD公司开发出了一款向下兼容x86CPU,向上又扩充了指令集,具有了64位CPU的特性,这款CPU后来改名为 ...
- 利用RestTemplate进行http调用
在对接API的时候,会涉及调用第三方的服务,这时候可以利用RestTemplate进行调用,下面给大家展示一个简单的调用demo. package com.tanlu.user.api.control ...
- 自定义函数导致的sql性能问题
同事说,某某报表跑的很慢,让我调查一下 优化前:该报表整体需要跑4小时以上. sql代码如下 SELECT /*省略多数查询字段*/ REP_FUN_REFCODEVALUE /*自定义函数*/ (P ...
- 常用mysql
搜索差价 select count(*) from gate_okex_cj; select * from (select count(*) as a_cj,coin from gate_okex_c ...
- 简单的自动化使用--使用selenium实现学习通网站的刷慕课程序。注释空格加代码大概200行不到
简单的自动化使用--使用selenium实现学习通网站的刷慕课程序.注释空格加代码大概200行不到 相见恨晚啊 github地址 环境Python3.6 + pycharm + chrom浏览器 + ...