打完这场,从紫名回到蓝名了
A. Misha and Forest
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output
Let‘s define a forest as a non-directed acyclic graph (also without loops and parallel edges). One day Misha played with the forest consisting of n vertices.
For each vertex v from 0 to n?-?1 he
wrote down two integers, degreev and sv,
were the first integer is the number of vertices adjacent to vertex v, and the second integer is the XOR sum of the numbers of vertices adjacent to v (if
there were no adjacent vertices, he wrote down 0).
Next day Misha couldn‘t remember what graph he initially had. Misha has values degreev and sv left,
though. Help him find the number of edges and the edges of the initial graph. It is guaranteed that there exists a forest that corresponds to the numbers written by Misha.
Input
The first line contains integer n (1?≤?n?≤?216),
the number of vertices in the graph.
The i-th of the next lines contains numbers degreei and si (0?≤?degreei?≤?n?-?1, 0?≤?si?<?216),
separated by a space.
Output
In the first line print number m, the number of edges of the graph.
Next print m lines, each containing two distinct numbers, a and b (0?≤?a?≤?n?-?1, 0?≤?b?≤?n?-?1),
corresponding to edge (a,?b).
Edges can be printed in any order; vertices of the edge can also be printed in any order.
Sample test(s)
input
3 2 3 1 0 1 0
output
2 1 0 2 0
input
2 1 1 1 0
output
1 0 1
考试中只做出来第一题。。
这道题就是利用了a^b^b=a这个性质。
每次对于度数只剩1的点x直接输出他的连边,并且把与x相连的这个点度数-1,并异或上x,那么就消除了x对与x相连的那个点的影响。
一开始没想到用队列来做。。一直WA+TLE..
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> using namespace std; int n,now,fr[100005],to[100005]; struct data { int d,s; }a[100005]; queue<int> q; int main() { scanf("%d",&n); for (int i=0;i<n;i++) { scanf("%d%d",&a[i].d,&a[i].s); now+=a[i].d; if (a[i].d==1) q.push(i); } cout<<now/2<<endl; now=0; while (!q.empty()) { int x=q.front(); q.pop(); if (a[x].d==1) { int v=a[x].s; fr[++now]=x,to[now]=v; a[v].d--; a[v].s^=x; if (a[v].d==1) q.push(v); } } for (int i=1;i<=now;i++) printf("%d %d\n",fr[i],to[i]); return 0; }
B. Misha and Permutations Summation
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
Let‘s define the sum of two permutations p and q of
numbers 0,?1,?...,?(n?-?1) as permutation ,
where Perm(x) is the x-th lexicographically
permutation of numbers 0,?1,?...,?(n?-?1) (counting from zero), and Ord(p) is
the number of permutation p in the lexicographical order.
For example, Perm(0)?=?(0,?1,?...,?n?-?2,?n?-?1), Perm(n!?-?1)?=?(n?-?1,?n?-?2,?...,?1,?0)
Misha has two permutations, p and q. Your task is
to find their sum.
Permutation a?=?(a0,?a1,?...,?an?-?1) is
called to be lexicographically smaller than permutation b?=?(b0,?b1,?...,?bn?-?1),
if for some k following conditions hold: a0?=?b0,?a1?=?b1,?...,?ak?-?1?=?bk?-?1,?ak?<?bk.
Input
The first line contains an integer n (1?≤?n?≤?200?000).
The second line contains n distinct integers from 0 to n?-?1,
separated by a space, forming permutation p.
The third line contains n distinct integers from 0 to n?-?1,
separated by spaces, forming permutation q.
Output
Print n distinct integers from 0 to n?-?1,
forming the sum of the given permutations. Separate the numbers by spaces.
Sample test(s)
input
2 0 1 0 1
output
0 1
input
2 0 1 1 0
output
1 0
input
3 1 2 0 2 1 0
output
1 0 2
Note
Permutations of numbers from 0 to 1 in the lexicographical order: (0,?1),?(1,?0).
In the first sample Ord(p)?=?0 and Ord(q)?=?0,
so the answer is .
In the second sample Ord(p)?=?0 and Ord(q)?=?1,
so the answer is .
Permutations of numbers from 0 to 2 in the lexicographical order: (0,?1,?2),?(0,?2,?1),?(1,?0,?2),?(1,?2,?0),?(2,?0,?1),?(2,?1,?0).
In the third sample Ord(p)?=?3 and Ord(q)?=?5,
so the answer is .
这道题考试中只想到O(nlog^2n)的做法,是二分+树状数组。与标解一步之遥。。
通过观察发现一个数列排在第几位,就是a1*(n-1)!+a2*(n-2)!+......an*0!,其中ai是指第i位所填的数比除掉之前所填的数
中剩下的数中ai个大,很明显要用树状数组。
两个数列的和其实就是ci=ai+bi,再处理一下进位就好(对于mod n!也就同时处理了)。
接下来就是如何通过ci来生成答案了。
我当时的想法是二分每一位(程序中被注释掉的部分),这样是超时的。。
应该充分利用一下树状数组的特点,有点像是倍增一样往上走,这样来做就是O(nlogn)了。(这个方法非常值得借鉴)
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; int v[200005],b[200005],t[200005],a[200005],x[200005],y[200005],now[200005],n; int lowbit(int x) { return x&(-x); } void Update(int x,int v) { for (int i=x;i<=n;i+=lowbit(i)) t[i]+=v; } int Getsum(int x) { int ans=0; for (int i=x-1;i;i-=lowbit(i)) ans+=t[i]; return ans; } /*int Get(int k) { int l=1,r=n; while (1) { int m=(l+r)>>1; int p=Getsum(m); if (p==k) { if (!v[m]) return m; else l=m+1; continue; } if (p<k) l=m; else r=m; } }*/ int Get(int k) { int ans=0,now=0; for (int i=20;i>=0;i--) if (ans+(1<<i)>n||now+t[ans+(1<<i)]>=k) continue; else { now+=t[ans+(1<<i)]; ans+=(1<<i); } return ans+1; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]++; for (int i=1;i<=n;i++) scanf("%d",&b[i]),b[i]++; for (int i=1;i<=n;i++) Update(i,1); for (int i=1;i<=n;i++) x[i]=Getsum(a[i]),Update(a[i],-1); for (int i=1;i<=n;i++) t[i]=0; for (int i=1;i<=n;i++) Update(i,1); for (int i=1;i<=n;i++) y[i]=Getsum(b[i]),Update(b[i],-1); for (int i=n;i>1;i--) { now[i]=now[i]+x[i]+y[i]; if (now[i]>n-i) now[i]=now[i]-n+i-1,now[i-1]++; } now[1]=now[1]+x[1]+y[1]; if (now[1]>=n) now[1]-=n; printf("%d ",now[1]); for (int i=1;i<=n;i++) t[i]=0; for (int i=1;i<=n;i++) Update(i,1); v[now[1]+1]=1; Update(now[1]+1,-1); for (int i=2;i<=n;i++) { int x=Get(now[i]+1); printf("%d ",x-1); Update(x,-1); v[x]=1; } return 0; }
感悟:
1.这次比赛感觉自己思路比较混乱,代码能力也很差。。
2.对于B题中对于答案数列的求解,逆用了树状数组的性质,就像是求一个二进制数一样求出了前缀和!!