2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分

    xiaoxiao2023-12-02  176

    题目链接:https://ac.nowcoder.com/acm/contest/903/E 题目大意:

    有n个男孩,索引从1到n,n个女孩索引从n+1到2n。

    有一天,他们在一起开派对。女孩们坐在第一排,男孩们坐在第二排。他们的坐姿是这样的:一个男孩坐在一个女孩的后面,一个男孩坐在最左边的椅子上,另一个男孩坐在第二个椅子上,等等。

    每个男孩都有一个他喜欢的女孩,他可以通过在一张纸上写下他想对她说的话来和他的爱人取得联系,并把它做成一个纸飞机,他会直接朝她扔去。你可以假设飞机会直线飞行。

    但是,当两个纸飞机在空中相撞并中途坠落时,问题就出现了。显然,这将是非常尴尬的。所以每个男孩都想知道,如果他扔纸飞机,有多少纸飞机有可能与它相撞。

    每个女孩都是一个男孩的心上人。

    输入: 第一行包含一个整数n。

    接下来是n行,第i行包含两个整数,第一个是坐在第i个男孩前面的女孩和他喜欢的女孩的索引。1≤n≤10^5

    输出n行,

    其中i-th包含一个整数,可能与i个男孩碰撞的飞机数。 思路: 我们把右边的点也记做1-n

    所有可以用主席树维护。每次把b[i]加进去。 查询[i, n]与[1,b[i]]匹配的数量: cx(root[i-1], 1, n, b[i]+1, n) 查询[0, i]与[b[i]+1, n]匹配的数量: cx(root[n], 1, n, 1, b[i]-1)-cx(root[i], 1, n, 1, b[i]-1);

    还可以用树状数组写,我当时没有想到,也是用差分的思想,本来省赛还补过,也是因为这题才学的主席树:https://blog.csdn.net/qq_21433411/article/details/89528291。

    因为这个查询是固定的所以可以边添加边查询,记录下来就ok了。

    for(int i=n;i>=1;i--)//查询0-b[i] -> 1的和就是b[i]+1 -> n与1-b[i] -> 1匹配的数量 { L[i]=cx(mp[b[i]]-1); add(mp[b[i]], 1); } for(int i=1;i<=n;i++)//查询b[i]+1 -> n的和就是1 -> a[i]-1与b[i]+1 -> n匹配的数量。 { R[i]=cx(n)-cx(mp[b[i]]); add(mp[b[i]], 1); } //树状数组 #include<bits/stdc++.h> #define LL long long using namespace std; int n, x, sum[50005]; int a[50005], b[50005]; int L[50005]={0}, R[50005]={0}; map<int, int> mp; void add(int p, int y)//p位置+y { while(p<=n) { sum[p]+=y; p+=(p&-p); } } int cx(int p) //前缀和 { int ans=0; while(p) { ans+=sum[p]; p-=(p&-p); } return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); mp[a[i]]=i; } memset(sum, 0, sizeof(sum)); for(int i=n;i>=1;i--) { L[i]=cx(mp[b[i]]-1); add(mp[b[i]], 1); } memset(sum, 0, sizeof(sum)); for(int i=1;i<=n;i++) { R[i]=cx(n)-cx(mp[b[i]]); add(mp[b[i]], 1); } for(int i=1;i<=n;i++) { printf("%d\n",L[i]+R[i]); } return 0; } //主席树 #include<bits/stdc++.h> #define LL long Long using namespace std; const int maxn=2e5+10; int L[maxn*20], R[maxn*20], sum[maxn*20]; int n, q, m ,cnt = 0; int a[maxn], b[maxn], root[maxn]; map<int ,int> mp; #define mid ((l+r)>>1) int JS(int l, int r) { int rt=++cnt; sum[rt]=0; if(l<r) { L[rt]=JS(l ,mid); R[rt]=JS(mid+1, r); } return rt; } int gx(int i, int l, int r, int x) { int rt=++cnt; L[rt]=L[i], R[rt]=R[i], sum[rt]=sum[i]+1;//更新 if(l<r) { if(x<=mid) { L[rt]=gx(L[i], l, mid, x); } else { R[rt]=gx(R[i], mid+1, r, x); } } return rt; } int ans=0; int cx(int i, int l, int r, int L1, int R1) { if(R1<L1) { return 0; } if(l==L1&&r==R1) { ans+=sum[i]; return 0; } if(R1<=mid) { cx(L[i], l, mid, L1, R1); } else if(L1>=(mid)+1) { cx(R[i], (mid)+1, r, L1, R1); } else { cx(L[i], l, mid, L1, mid); cx(R[i], (mid)+1, r, (mid)+1, R1); } } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i], &b[i]); mp[a[i]]=i; } for(int i=1;i<=n;i++) { b[i]=mp[b[i]]; } root[0] = JS(1, n);//建空树 for(int i=1;i<=n;i++) { //cout<<b[i]<<endl; root[i]=gx(root[i-1], 1, n, b[i]);//更新 } for(int i=1;i<=n;i++) { int s=0; ans=0; cx(root[i-1], 1, n, b[i]+1, n); s+=ans; ans=0; cx(root[n], 1, n, 1, b[i]-1); s+=ans; ans=0; cx(root[i], 1, n, 1, b[i]-1); s-=ans; printf("%d\n",s); } return 0; }
    最新回复(0)