Atcoder Grand Contest 023
A
略
B
略
C(计数)
题意:
有n个白球排成一行,故有n-1个空隙,我可以给一个空隙对应的两个白球都涂黑。n-1个空隙的一个排列就对应着一个涂黑顺序,定义这个涂黑顺序的价值是“将所有n个球都涂黑的最少步数”。对于n-1的所有排列,我们要求对应价值的和。
n<=1e6
分析:
首先易得最少步数一定在ceil(n/2)到n-1之间,我们去枚举最少步数,然后算对应的排列有多少个
设f(i)表示最少步数为i的排列有多少个
我们去观察n-1个空隙,假设我们取的空隙从小到大编号依次是$x_1,x_2,x_3,......,x_i$
那么x1一定是1,xi一定是n-1,并且满足$x_{i+1}-x_i=1 or 2$
那取哪些位置可以通过组合数算出来,然后它们在排列一下,剩下的(n-1-i)个无关空隙也丢在后面排列一下,就能计算了
D(贪心)
题意:
在一个数轴上有n个点,表示n个公寓,第i个公寓的坐标是xi,住了pi个人。这些人都是公司的员工,公司的坐标在S。
现在下班了,所有人坐上了大巴车,大巴车从S出发,大巴的速度是固定的,每秒1个单位长度。每次车上的人都进行投票,选择大巴是向左开还是向右开(平票就向左)。每个人的投票原则就是要让自己尽量早到家,并且每个人都是极其聪明的。大巴在到达了一个坐标之后,家住在这的人都会立刻下车。
问大巴会运行多长时间。
n<=1e5,pi<=1e9,坐标<=1e9
分析:
如果S在所有点的一侧,那么很显然就是直接一路开过去。
如果S在这些点的中间位置,那么显然下车顺序一定是从中间到两边不断吞噬。
我们来考察一下最外面的两个点1和n,不妨设p[1]>=p[n]
这样的话,在到达第n个点之前,一定到达了第1个点(假设现在在第n-1个点且第1个点还没到达,那么第一个点人多,所以他们会投票往左开)。在到达了第1个点后,再一路向右开。也就是说time[n]=time[1]+x[n]-x[1]
换句话说,住在第n个点人的到家时间是第1个点的人到家时间加上一个常数,第n个点的人希望自己到家时间尽量短,也就是希望第1个点的人到家时间尽量短,也就是说第n个点的人的投票一定是和第一个点的人投票是一样的
那这样我们就可以看成只有1~n-1个点,将p[n]加到[1]上,缩小了问题规模
p[1]<p[n]也是同理
这样不断从两边向中间缩小规模,最后得到一个点,直接从S开到这个点就行了,在贪心过程中记录下时间的计算关系,计算下时间就行了。
注意一下细节,在缩小规模的过程中,如果出现了S在目前所有点的一侧,要特殊处理后面的所有过程。
时间复杂度O(n)
- #include<bits/stdc++.h>
- using namespace std;
- const int maxn=1e5;
- typedef long long ll;
- #define mp make_pair
- ll x[maxn+],p[maxn+];
- vector<pair<int,ll> > g[maxn+];
- ll ans[maxn+];
- int n,s;
- void addedge(int u,int v,ll w)
- {
- g[u].push_back(mp(v,w));
- }
- void dfs(int k)
- {
- for(auto x:g[k])
- {
- ans[x.first]=ans[k]+x.second;
- dfs(x.first);
- }
- }
- int main()
- {
- scanf("%d%d",&n,&s);
- for(int i=;i<=n;++i) scanf("%lld%lld",&x[i],&p[i]);
- int l=,r=n;
- while(l<r)
- {
- if(x[l]>=s) addedge(l,l+,x[l+]-x[l]),++l;
- else
- if(x[r]<=s) addedge(r,r-,x[r]-x[r-]),--r;
- else
- if(p[l]>=p[r]) p[l]+=p[r],addedge(l,r,x[r]-x[l]),--r;
- else p[r]+=p[l],addedge(r,l,x[r]-x[l]),++l;
- }
- ans[l]=abs(x[l]-s);
- dfs(l);
- printf("%lld\n",max(ans[],ans[n]));
- return ;
- }
E(计数)
题意:
给出a1~an,表示每一个位置的上限,在满足这个上限要求的所有n的排列中,求逆序对个数的和。
n<=2e5
分析:
首先需要会两个子问题:
1、给定上限要求a1~an,一共有多少个排列满足这个上限限制呢?
记cnt[k]=(满足ai>=k的个数)-(n-k),那么排列个数就是cnt[1]*cnt[2]*...*cnt[n]。这是因为我们从大到小去安排,对于最大的数字n,我们看看它有多少个位置可以放;对于次大的数字n-1,我们看看它有多少个位置可以放(注意之前在某个位置已经放过了一个n)……。这样总排列数就是所有cnt的乘积。
2、如何求n的所有排列的逆序对的个数和?
算任意两个位置的贡献。考虑枚举两个位置i和j(i<j),我们去计算有多少个排列满足pi>pj。那么怎么计算有多少个排列满足这个条件呢?考虑对称性,pi>pj的排列个数=pi<pj的排列个数,故pi<pj的排列个数就是总排列个数的一半,也就是n!/2
这个思想就是解决这个问题的关键。当然如果计算逆序对个数和的话,再化化简就能得出一个通式,这里就不推导了。
现在回到这个问题,我们假设满足限制的排列个数是S(如果S是0,就直接返回0了)。
我们去枚举任意两个位置i,j(i<j),我们去计算有多少个排列满足pi>pj,且满足a[i]的限制
①对于ai<=aj的情况,相当于把aj变成ai,然后求有多少个排列满足这个限制,再除以2。我们考虑把aj变成ai会带来什么影响,其实就是cnt[a[i]+1]~cnt[a[j]]这一段都减去了1,我们可以用一个D[]来表示(cnt[i]-1)/cnt[i]的前缀积,那么答案就是1/2*S*D[a[j]]/D[a[i]]。这样时间复杂度是O(n^2)的,但是很显然这个是可以用bit来维护的,O(nlogn)
②对于ai>aj的情况,处理思路也是差不多的,但这个时候就要计算反面,即满足i<j,ai>aj,pi>pj的排列数等于总排列数S-"满足i<j,ai>aj,pi<pj的排列数",后面这个东西的处理和第一种情况是类似的。
但还有一个麻烦的问题,就是D[x]/D[y]可能会出现分母为0的情况,我们来考虑它的实际意义
0 0 1 2 3 0 2
比如这里D[5]/D[3]虽然是0/0,但是是合法的,它表示将[4,5]这一段的cnt用cnt-1替换,我们可以将每一个D[i]看做是$D[i]*0^{x[i]}$,若作除法的两个D的0上指标相同,那就可以得到正确结果,否则就得到0。
那么怎么具体实现呢?注意到指标相同的两个数一定是这个数组里连续的无0的一段,所以我们可以预处理出每个位置的pre[]和suf[],表示0的上指标相同的左右位置极限,然后在树状数组求解的时候强化一下询问范围就行了。
时间复杂度O(nlogn)
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int maxn=2e5;
- const ll mod=1e9+;
- ll a[maxn+],num[maxn+],cnt[maxn+],S,D[maxn+];
- int pre[maxn+],suf[maxn+];
- int n;
- ll c[][maxn+];
- ll ans=;
- ll Pow(ll a,ll b,ll mod)
- {
- ll ans=;
- while(b)
- {
- if(b&) ans=ans*a%mod;
- a=a*a%mod;
- b>>=;
- }
- return ans;
- }
- ll inv(ll a)
- {
- return Pow(a,mod-,mod);
- }
- int lowbit(int x)
- {
- return x&(-x);
- }
- ll add(ll *c,int k,ll data)
- {
- for(;k<=n+;k+=lowbit(k)) c[k]=(c[k]+data)%mod;
- }
- ll query(ll *c,int k)
- {
- ll ans=;
- for(;k;k-=lowbit(k)) ans=(ans+c[k])%mod;
- return ans;
- }
- int main()
- {
- scanf("%d",&n);
- for(int i=;i<=n;++i) scanf("%lld",&a[i]),++num[a[i]];
- for(int i=n-;i>=;--i) num[i]+=num[i+];
- S=;
- D[]=;
- for(int i=;i<=n;++i)
- {
- cnt[i]=num[i]-(n-i),S=S*cnt[i]%mod;
- if(cnt[i]<=) return *printf("0\n");
- D[i]=D[i-];
- if(cnt[i]>) D[i]=D[i]*(cnt[i]-)%mod*inv(cnt[i])%mod,pre[i]=pre[i-];else pre[i]=i;
- }
- suf[n]=n;
- for(int i=n-;i>=;--i)
- if(cnt[i]>)
- {
- if(cnt[i+]>) suf[i]=suf[i+];else suf[i]=i;
- }
- else suf[i]=i;
- for(int i=;i<=n;++i)
- {
- ans=(ans+(query(c[],a[i])-query(c[],pre[a[i]]-))%mod*S%mod*D[a[i]]%mod*inv(2LL)%mod)%mod;
- if(ans<) ans+=mod;
- add(c[],a[i],inv(D[a[i]]));
- }
- for(int i=;i<=n;++i) c[][i]=;
- for(int i=;i<=n;++i)
- {
- ans=(ans+S*(query(c[],n)-query(c[],a[i]))%mod-inv(2LL)*S%mod*inv(D[a[i]])%mod*(query(c[],suf[a[i]])-query(c[],a[i]))%mod)%mod;
- if(ans<) ans+=mod;
- add(c[],a[i],D[a[i]]);
- add(c[],a[i],1LL);
- }
- printf("%lld\n",ans);
- return ;
- }
F(贪心)
题意:
给一个n个点的树,每个点表示一个数字0/1。你需要找出一个拓扑序,使得这个拓扑序对应的01序列逆序对个数尽量少。
n<=2e5
分析:
0应该尽量放前面,于是我们在树中找一个是0的点,让它和其父亲绑定。(即我们希望它的父亲出现后,紧跟在后出现的就是这个点)
假设这样的限制都弄出来了,会有一些互相无限制的点集,那么我们怎么决定先后取的顺序呢?
假设有这样两个点集a和b,a里有a0个0,a1个1,b中有b0个0,b1个1
那么如果ab比ba优秀,那么一定有a1*b0<a0*b1,即a1/a0<b1/b0
于是我们的贪心方法出来了,刚开始,每个点自己作为一个点集,我们记录每个点集的cnt0和cnt1。
然后我们选出cnt1/cnt0最小的点集,把这个点集和其父亲所在的点集合并就行了,一直合并只剩最后一个根节点
用set实现就行了
时间复杂度O(nlogn)
- #include<bits/stdc++.h>
- using namespace std;
- const int maxn=2e5;
- struct wjmzbmr
- {
- int c0,c1,id;
- wjmzbmr(){}
- wjmzbmr(int a,int b,int c):c0(a),c1(b),id(c) {}
- bool operator < (const wjmzbmr& x) const
- {
- if(1LL*c0*x.c1!=1LL*c1*x.c0) return 1LL*c0*x.c1>1LL*c1*x.c0;
- return id<x.id;
- }
- }a[maxn+];
- int p[maxn+],f[maxn+],tmp[maxn+],tail[maxn+],nx[maxn+],c0[maxn+],c1[maxn+],d[maxn+];
- int n,root;
- int b[maxn+];
- int c[maxn+];
- long long ans=;
- multiset<wjmzbmr> s;
- int find(int x)
- {
- if(f[x]==x) return f[x];else return f[x]=find(f[x]);
- }
- int main()
- {
- scanf("%d",&n);
- for(int i=;i<=n;++i) scanf("%d",&p[i]);
- for(int i=;i<=n;++i)
- {
- scanf("%d",&tmp[i]);
- f[i]=i,tail[i]=i;
- if(tmp[i]==) a[i]={,,i},c0[i]=;else a[i]={,,i},c1[i]=;
- if(i>=)
- s.insert(a[i]);
- }
- while(s.size()>)
- {
- wjmzbmr x=*s.begin();
- s.erase(s.begin());
- int u=x.id;
- int v=find(p[x.id]);
- nx[tail[v]]=u;
- ++d[u];
- f[u]=v,tail[v]=tail[u];
- if(v!=) s.erase(s.find(wjmzbmr(c0[v],c1[v],v)));
- c0[v]+=c0[u],c1[v]+=c1[u];
- if(v!=) s.insert(wjmzbmr(c0[v],c1[v],v));
- }
- for(int i=;i<=n;++i)
- if(d[i]==)
- {
- root=i;
- break;
- }
- for(int i=;i<=n;++i,root=nx[root])
- {
- b[i]=tmp[root];
- }
- int now=;
- for(int i=n;i>=;--i)
- if(b[i]==) ans+=now;else now+=;
- printf("%lld\n",ans);
- return ;
- }
Atcoder Grand Contest 023的更多相关文章
- AtCoder Grand Contest 023 A - Zero-Sum Ranges
Time limit : 2sec / Memory limit : 256MB Score : 200 points Problem Statement We have an integer seq ...
- Atcoder Grand Contest 023 E - Inversions(线段树+扫描线)
洛谷题面传送门 & Atcoder 题面传送门 毒瘤 jxd 作业-- 首先我们不能直接对所有排列计算贡献对吧,这样复杂度肯定吃不消,因此我们考虑对每两个位置 \(x,y(x<y)\), ...
- AtCoder Grand Contest 023 E - Inversions
Description 给出长度为 \(n\) 序列 \(A_i\),求出所有长度为 \(n\) 的排列 \(P\),满足 \(P_i<=A_i\),求所有满足条件的 \(P\) 的逆序对数之和 ...
- AtCoder Grand Contest 023 C - Painting Machines
Description 一个长度为 \(n\) 的序列,初始都为 \(0\),你需要求出一个长度为 \(n-1\) 的排列 \(P\), 按照 \(1\) 到 \(n\) 的顺序,每次把 \(P_i\ ...
- AtCoder Grand Contest 023 F - 01 on Tree
Description 题面 Solution HNOI-day2-t2 复制上去,删点东西,即可 \(AC\) #include<bits/stdc++.h> using namespa ...
- AtCoder Grand Contest 012
AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...
- AtCoder Grand Contest 011
AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...
- AtCoder Grand Contest 031 简要题解
AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...
- AtCoder Grand Contest 010
AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...
随机推荐
- 关于jQuery中的$发生冲突及解决方案
问题描述: 在Jquery库中,$是JQuery的别名,所有使用$的地方也都可以使用JQuery来替换,如$('#msg')等同于JQuery('#msg')的写法. 当引入多个js库后,其它的js库 ...
- Spring框架 aop中的操作术语
Joinpoint 连接点 Pointcut 切入点 Advice 通知/增强 举例: 后置通知,不抛出异常则执行此通知,抛异常则不执行 最终通知,抛不抛异常都通知 其他通知都是环绕通知的衍生 ...
- C语言运算符_03
·运算符的优先级:C语言中,运算符的优先级共分为15级.1级最高,15级最低.在表达式中,优先级较高的先于优先级较低的进行运算.而在同一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合 ...
- nginx 部署ssl证书之后访问用火狐出现SSL_ERROR_RX_RECORD_TOO_LONG此错误,用Google出现ERR_SSL_PROTOCOL_ERROR错误
server { listen ; server_name xxx.com; ssl_certificate ssl/xxx.pem; ssl_certificate_key ssl/xxx.key; ...
- Could not connect to Redis at IP No route to host
这个问题是在用远程去访问redis出现的 原因:是服务器新装系统 iptables这个的问题 解决办法: sudo iptables -F 轻松解决
- java之 单根继承与集合
1.单根继承 概念: 单根继承,意味着所有类的继承,都继承自单一的基类的继承模式 优点: (1)所有对象都具有一个共用接口,归根到底都是相同的基本类型. (1)所有对象都具有一个共用接口,归根到底都是 ...
- Codeforces Round #439 (Div. 2) B. The Eternal Immortality
B. The Eternal Immortality 题目链接http://codeforces.com/contest/869/problem/B 解题心得:题意就是给出a,b,问(a!)/(b!) ...
- 流编辑器sed知识点总结
sed(流文本编辑器) 每次读取一行到模式空间中, 修改的sed模式空间中的内容,并不会修改源文件, 继而输出模式空间的内容, 最后删除模式空间中的内容. sed [O ...
- C++ 实验六
Part.2 // 合并两个文件内容到一个新文件中. // 文件名均从键盘输入 #include <iostream> #include <fstream> #include ...
- Mysql 使用命令及 sql 语句示例
Mysql 是数据库开发使用的主要平台之一.sql 的学习掌握与使用是数据库开发的基础,此处展示详细sql 语句的写法,及各种功能下的 sql 语句. 在此处有 sql 语句使用示例:在这里 此处插入 ...