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的更多相关文章

  1. AtCoder Grand Contest 023 A - Zero-Sum Ranges

    Time limit : 2sec / Memory limit : 256MB Score : 200 points Problem Statement We have an integer seq ...

  2. Atcoder Grand Contest 023 E - Inversions(线段树+扫描线)

    洛谷题面传送门 & Atcoder 题面传送门 毒瘤 jxd 作业-- 首先我们不能直接对所有排列计算贡献对吧,这样复杂度肯定吃不消,因此我们考虑对每两个位置 \(x,y(x<y)\), ...

  3. AtCoder Grand Contest 023 E - Inversions

    Description 给出长度为 \(n\) 序列 \(A_i\),求出所有长度为 \(n\) 的排列 \(P\),满足 \(P_i<=A_i\),求所有满足条件的 \(P\) 的逆序对数之和 ...

  4. AtCoder Grand Contest 023 C - Painting Machines

    Description 一个长度为 \(n\) 的序列,初始都为 \(0\),你需要求出一个长度为 \(n-1\) 的排列 \(P\), 按照 \(1\) 到 \(n\) 的顺序,每次把 \(P_i\ ...

  5. AtCoder Grand Contest 023 F - 01 on Tree

    Description 题面 Solution HNOI-day2-t2 复制上去,删点东西,即可 \(AC\) #include<bits/stdc++.h> using namespa ...

  6. AtCoder Grand Contest 012

    AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...

  7. AtCoder Grand Contest 011

    AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...

  8. AtCoder Grand Contest 031 简要题解

    AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...

  9. AtCoder Grand Contest 010

    AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...

随机推荐

  1. 洛谷 P1019 单词接龙 (DFS)

    题目传送门 当时一看到这题,蒟蒻的我还以为是DP,结果发现标签是搜索-- 这道题的难点在于思路和预处理,真正的搜索实现起来并不难.我们可以用一个贪心的思路,开一个dic数组记录每个单词的最小重复部分, ...

  2. iOS之WKWebView

    Xcode8发布以后,编译器开始不支持IOS7,所以很多应用在适配IOS10之后都不在适配IOS7了,其中包括了很多大公司,网易新闻,滴滴出行等.因此,我们公司的应用也打算淘汰IOS7. 支持到IOS ...

  3. Swift 编程思想 Part 4:map all the things!

    Swift 编程思想 Part 4:map all the things! 2015-10-22  837 文章目录 1. 数组 vs. 可选类型 2. 作用在可选类型上的 map() 3. 回到我们 ...

  4. history.pushState 实现浏览器页面不刷新修改url链接

    最近遇到了在不刷新页面的情况下修改浏览器url链接的需求,遂求助于万能的度娘,最终通过history.pushState()完美解决问题.现在将我理解的一些内容分享一下,不对的地方欢迎大家指出. 在使 ...

  5. MySql中引擎

    1. InnoDB 引擎 MySQL 5.5 及以后版本中的默认存储引擎,它的优点如下:灾难恢复性好,支持事务,使用行级锁,支持外键关联,支持热备份. InnoDB引擎中的表,其数据的物理组织形式是簇 ...

  6. Flask扩展实现HTTP令牌token认证HTTPTokenAuth

    Token认证 在restful设计中,用户认证模式通常使用json web token,而不会使用传统的HTTP Basic认证(传入账号密码) token认证模式如下:在请求header中加入to ...

  7. LeetCode(109) Convert Sorted List to Binary Search Tree

    题目 Given a singly linked list where elements are sorted in ascending order, convert it to a height b ...

  8. Hi3519V101 Uboot和Kernel编译

    前面已经搭建好了Ubuntu下的海思开发环境,现在对编译Uboot和Kernel的过程做一个简单的记录.参考文档<Hi3519V101 U-boot 移植应用开发指南.pdf>和<H ...

  9. PAT Basic 1015

    1015 德才论 宋代史学家司马光在<资治通鉴>中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人.凡取人之术,苟不得圣人,君子而与之,与其 ...

  10. Python 描述符(Descriptor) 附实例

    在 Python 众多原生特性中,描述符可能是最少被自定义的特性之一,但它在底层实现的方法和属性却无时不刻被使用着,它优雅的实现方式体现出 Python 简洁之美. 定义 一个描述符是一个有" ...