XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki
A. Ability Draft
记忆化搜索。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n, m, S, g; int f[100][1<<10]; int p[100]; int s[100][10]; int sm, bg; int smp, bgp; int smk[100]; int bgk[100]; int cal(int o, int sta) { if(o > g)return 0; if(~f[o][sta])return f[o][sta]; int x = p[o]; int y = p[o + 1]; f[o][sta] = -2e9; int canbig = ((sta >> x & 1) == 0); int cansml = S - (s[o - 1][x] - (canbig == 0)); //canbig == 0 means we picked ulti int sgn = (x / n == y / n) ? 1 : -1; if(canbig) { int add = bgk[++bgp]; f[o][sta] = max(sgn * cal(o + 1, sta | 1 << x) + add, f[o][sta]); --bgp; } if(cansml) { int add = smk[++smp]; f[o][sta] = max(sgn * cal(o + 1, sta) + add, f[o][sta]); --smp; } return f[o][sta]; } int main() { while(~scanf("%d%d",&n, &S)) { m = n * 2; g = m * (S + 1); for(int i = 1; i <= g; ++i) { scanf("%d", &p[i]); --p[i]; for(int j = 0; j < m; ++j) { s[i][j] = s[i - 1][j] + (j == p[i]); } } scanf("%d", &sm); for(int i = 1; i <= sm; ++i)scanf("%d", &smk[i]); sort(smk + 1, smk + sm + 1); reverse(smk + 1, smk + sm + 1); scanf("%d", &bg); for(int i = 1; i <= bg; ++i)scanf("%d", &bgk[i]); sort(bgk + 1, bgk + bg + 1); reverse(bgk + 1, bgk + bg + 1); smp = bgp = 0; MS(f, -1); printf("%d\n", cal(1, 0)*((p[1]/n==0)?1:-1)); } return 0; } /* 【trick&&吐槽】 1 1 1 2 2 1 2 5 3 2 7 2 1 2 2 1 1 2 2 1 4 4 8 8 9 2 6 7 (ans = 2) 2 1 1 3 4 2 2 4 3 1 6 1 4 4 8 9 11 5 14 11 10 8 5 【题意】 【分析】 【时间复杂度&&优化】 */
B. Short Random Problem
积分DP。
C. Block, Stock and Two Smoking Galaxy Notes
枚举领导者$S$,它需要满足度数至少为$\frac{n}{2}$。
枚举完领导后,将和$S$认识和不认识的分成两组二分图匹配即可。
时间复杂度$O(m^2)$。
#include<cstdio> #include<cstdlib> using namespace std; const int N=1010,M=10010; int n,m,i,e[M][2],g[N],v[M],nxt[M],ed,f[N],b[N],T,right[N],deg[N]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} bool find(int x){ for(int i=g[x];i;i=nxt[i])if(b[v[i]]<T){ b[v[i]]=T; if(!f[v[i]]||find(f[v[i]]))return f[v[i]]=x,1; } return 0; } void solve(int S){ int i; for(i=1;i<=n;i++)right[i]=g[i]=0; ed=0; for(i=1;i<=m;i++){ if(e[i][0]==S)right[e[i][1]]=1; if(e[i][1]==S)right[e[i][0]]=1; } for(i=1;i<=m;i++){ if(e[i][0]!=S&&!right[e[i][0]]&&right[e[i][1]])add(e[i][0],e[i][1]); if(e[i][1]!=S&&!right[e[i][1]]&&right[e[i][0]])add(e[i][1],e[i][0]); } for(i=1;i<=n;i++)f[i]=b[i]=0; T=0; for(i=1;i<=n;i++){ if(right[i]||i==S)continue; T++; if(!find(i))return; } puts("Yes"); int cnt=0; for(i=1;i<=n;i++)if(right[i])cnt++; printf("%d %d\n",S,cnt); for(i=1;i<=n;i++)if(right[i]){ printf("%d ",i); if(!f[i])f[i]=-1; printf("%d\n",f[i]); } exit(0); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++)scanf("%d%d",&e[i][0],&e[i][1]),deg[e[i][0]]++,deg[e[i][1]]++; for(i=1;i<=n;i++)if(deg[i]>=n/2-5)solve(i); puts("No"); } /* 5 4 1 2 2 3 3 4 4 5 */
D. Lunch Queue
用一棵平衡树$S$维护整个队列,再对每个队伍用一棵平衡树$T_i$维护。
那么对于新来的一个人,先在$S$中找出满足距离限制最紧的那个点$x$,再在$T_{c_i}$中查找$x$之后最靠前的点作为插队位置。
瓶颈在于最后一步比较两个人在$S$中的前后关系,将$S$用替罪羊树维护,同时维护动态标号即可$O(1)$比较。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=400010; typedef unsigned long long ll; const ll inf=1ULL<<63; const double A=0.8; ll tl[N],tr[N],tm[N]; int size[N],son[N][2],f[N],tot,root; int id[N],cnt; int n,i,a[N]; void dfs(int x){ if(son[x][0])dfs(son[x][0]); id[++cnt]=x; if(son[x][1])dfs(son[x][1]); } int build(int fa,int l,int r,ll a,ll b){ int mid=(l+r)>>1,x=id[mid]; f[x]=fa;son[x][0]=son[x][1]=0;size[x]=1;tl[x]=a;tr[x]=b;tm[x]=(a+b)>>1; if(l==r)return x; if(l<mid)size[x]+=size[son[x][0]=build(x,l,mid-1,a,tm[x])]; if(r>mid)size[x]+=size[son[x][1]=build(x,mid+1,r,tm[x],b)]; return x; } inline int kth(int k){ if(k<1)return 0; int x=root,rank; while(1){ rank=size[son[x][0]]+1; if(k==rank)return x; if(k<rank)x=son[x][0];else k-=rank,x=son[x][1]; } } inline int rebuild(int x){ cnt=0; dfs(x); return build(f[x],1,cnt,tl[x],tr[x]); } inline void fix(int x){ int deep=1,z=x; size[x]++; while(f[z])size[z=f[z]]++,deep++; if(deep<log(tot)/log(1/A))return; while((double)size[son[x][0]]<A*size[x]&&(double)size[son[x][1]]<A*size[x])x=f[x]; if(!x)return; if(x==root){root=rebuild(x);return;} int y=f[x],b=son[y][1]==x,now=rebuild(x); son[y][b]=now; } inline void ins(int A,int B,int x){ if(!root){ root=size[1]=1; tr[1]=inf; tm[1]=inf>>1; return; } while(1){ if(!son[A][B]){ son[A][B]=x; f[x]=A; if(!B){ tl[x]=tl[A]; tr[x]=tm[A]; }else{ tl[x]=tm[A]; tr[x]=tr[A]; } tm[x]=(tl[x]+tr[x])>>1; break; } A=son[A][B]; } fix(x); } inline void insd(int A,int B,int x){ if(!son[A][B]){ son[A][B]=x; f[x]=A; if(!B){ tl[x]=tl[A]; tr[x]=tm[A]; }else{ tl[x]=tm[A]; tr[x]=tr[A]; } tm[x]=(tl[x]+tr[x])>>1; fix(x); return; } ins(son[A][B],B^1,x); } void show(int x){ if(son[x][0])show(son[x][0]); printf("%d ",x); if(son[x][1])show(son[x][1]); } namespace DS{ int root[N],son[N][2],f[N]; inline void rotate(int x){ int y=f[x],w=son[y][1]==x; son[y][w]=son[x][w^1]; if(son[x][w^1])f[son[x][w^1]]=y; if(f[y]){ int z=f[y]; if(son[z][0]==y)son[z][0]=x; else if(son[z][1]==y)son[z][1]=x; } f[x]=f[y];son[x][w^1]=y;f[y]=x; } inline void splay(int x){ while(f[x]){ int y=f[x]; if(f[y]){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} rotate(x); } } inline void insert(int&x,int y){ if(!x){x=y;return;} int z=x; while(1){ int b=tm[y]>tm[z]; if(son[z][b])z=son[z][b]; else{ son[z][b]=y; f[y]=z; break; } } splay(x=y); } int ask(int&o,int y){ int t=0,z=0,x=o; while(x){ z=x; if(tm[x]>tm[y]){ t=x; x=son[x][0]; }else{ x=son[x][1]; } } if(z)splay(o=z); return t; } } int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ int x,dis; scanf("%d%d",&x,&dis); a[i]=x; if(i==1)ins(0,0,i); else{ int y=kth(i-dis-1); if(a[y]==x)insd(y,1,i); else{ int z=DS::ask(DS::root[x],y); if(z)insd(z,0,i); else ins(root,1,i); } } DS::insert(DS::root[x],i); } show(root); }
E. Oneness
$ans=\lfloor\frac{n}{11}\rfloor+\lfloor\frac{n}{111}\rfloor+\lfloor\frac{n}{1111}\rfloor+...$。
令$N=9n$,则$ans=\lfloor\frac{N}{99}\rfloor+\lfloor\frac{N}{999}\rfloor+\lfloor\frac{N}{9999}\rfloor+...$。
对于整数部分FFT计算答案,对于小数部分用实数近似计算即可。
时间复杂度$O(|n|\log |n|)$。
F. Shuffle
对于置换中的每个循环,通过KMP求出所有可能的匹配位置,它们必然是一个等差数列。
那么对于所有循环分别列同余方程,然后扩展欧几里得求解即可。
时间复杂度$O(n)$。
#include<cstdio> #include<cstring> #include<cstdlib> using namespace std; typedef int ll; const int N=1000010; int n,i,j,p[N],vis[N],q[N]; char a[N],b[N],c[N],S[N],T[N]; int nxt[N],w[N*4]; int ans=1; int _a[N],_b[N],tot; namespace NT{ int flag=1; ll k=1,m,a,r,d,x,y; ll exgcd(ll a,ll b,ll&x,ll&y){ if(!b)return x=1,y=0,a; ll d=exgcd(b,a%b,x,y),t=x; return x=y,y=t-a/b*y,d; } inline void add(ll a,ll r){ if(r>=a)while(1); r%=a; if(!flag)return; d=exgcd(k,a,x,y); if((r-m)%d){flag=0;return;} x=(x*(r-m)/d+a/d)%(a/d),y=k/d*a,m=((x*k+m)%y)%y; if(m<0)m+=y; k=y; } void write(ll x){ if(x>=10)write(x/10); int t=x%10; printf("%d",t); } void show(){ if(flag)write(m); else puts("-1"); } } inline void solve(int len){ int i,j,k,cnt=0; for(i=1;i<=len;i++)T[i]=b[q[i]],S[i]=a[q[i]]; //printf("len=%d\n",len); //for(i=1;i<=len;i++)putchar(T[i]);puts(""); //for(i=1;i<=len;i++)putchar(S[i]);puts(""); for(nxt[1]=j=0,i=2;i<=len;nxt[i++]=j){ while(j&&S[j+1]!=S[i])j=nxt[j]; if(S[j+1]==S[i])j++; } //for(i=1;i<=len;i++)printf("nxt[%d]=%d\n",i,nxt[i]); for(i=1,j=0,k=1;i<=len*4;i++){ while(j&&S[j+1]!=T[k])j=nxt[j]; if(S[j+1]==T[k]){ j++; if(j==len){ w[++cnt]=i-len; j=nxt[j]; //printf("find %d\n",i); } } k++; if(k>len)k=1; } if(!cnt){ puts("-1"); exit(0); } if(cnt<2)while(1); for(i=2;i<=cnt;i++)if(w[i]-w[i-1]!=w[2]-w[1])while(1); //printf("per=%d occ=%d\n",w[2]-w[1],w[1]); NT::add(w[2]-w[1],w[1]); } int gcd(int a,int b){return b?gcd(b,a%b):a;} int main(){ scanf("%s%s",a+1,b+1); n=strlen(a+1); for(i=1;i<=n;i++){ int x; if(i<=n/2)x=i*2-1; else x=i*2-n; //printf("! %d %d\n",i,x); p[x]=i; } for(i=1;i<=n;i++)if(!vis[i]){ int cnt=0; for(j=i;!vis[j];j=p[j])vis[j]=1,q[++cnt]=j; //for(j=1;j<=cnt;j++)printf("%d ",q[j]);puts(""); solve(cnt); } NT::show(); } /* aaababbbbbbabbbbab aabbabbababbbabbbb babaaabbaa bbaaaababa */
G. Piecewise Linearity
按题意反解出每个函数即可,精度可以取模控制,不过使用__float128也可以通过全部数据。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; double xx[N], yy[N]; __float128 x[N], y[N], v[N], k[N]; const double EPS = 1e-9; __float128 fabs(__float128 x) { return x >= 0 ? x : -x; } bool same(__float128 x, __float128 y) { x -= y; return x <= EPS && x >= -EPS; } int main() { while(~scanf("%d",&n)) { for(int i = 1; i <= n + 1; ++i) { scanf("%lf%lf", &xx[i], &yy[i]); x[i] = xx[i]; y[i] = yy[i]; } __float128 sum = 0; __float128 yy = 0; for(int i = 1; i <= n; ++i) { k[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]); if(i >= 2) { v[i] = (k[i] - k[i - 1]) / 2; sum += v[i]; yy += v[i] * fabs(x[1] - x[i]); } } if(!same(sum, k[n]) || !same(-sum, k[1]) || !same(yy, y[1])) { puts("No"); } else { puts("Yes"); } } return 0; } /* 【trick&&吐槽】 2 -1 2 1 0 2 1 3 -3 -1 -1 -1 1 1 4 1 3 -3 1 -2 0 0 1 1 1 【题意】 【分析】 【时间复杂度&&优化】 */
H. Sketch
贪心将原序列复制下来,然后构造递减数列,注意特判原序列非法的情况。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 3e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n, m, k; int a[N], b[N]; int main() { while(~scanf("%d%d%d", &k, &n, &m)){ a[0] = 1; for(int i = 1; i <= k; i ++) { scanf("%d", &a[i]); if(a[i] == -1){ a[i] = a[i - 1]; } } int flag = 1; for(int i = 2; i <= k; i ++){ if(a[i] < a[i - 1]) flag = 0; } if(k > n || !flag){ puts("-1"); continue; } int rst = n - k, o = 0; for(int i = 1; i <= k; i ++){ int x = m; while(rst > 0){ if(x == a[i]) break; b[++ o] = x --; rst --; } b[++ o] = a[i]; } /* if(rst){ int x = b[o] - 1; while(rst > 0){ if(x == 0) break; b[++ o] = x --; rst --; } } */ if(rst){ puts("-1"); } else{ for(int i = 1; i <= n; i ++) printf("%d ", b[i]); puts(""); } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
I. $\leq$ or $\geq$
最优策略:将元素个数为$k$的栈的栈顶元素的权重设置为$2^{k-1}$,然后取加权中位数。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n, k, x; int a[N], b[N]; char s[10]; int rst[N]; int w[20]; typedef pair<int,int>P; P q[N]; LL pre[N][2],suf[N][2]; int main() { w[1]=1; w[2]=2; w[3]=4; w[4]=8; w[5]=16; w[6]=32; w[7]=64; w[8]=128; w[9]=256; w[10]=512; while(~scanf("%d%d",&n, &k)) { for(int i = 1; i <= n; i ++) rst[i] = k; while(1){ int num = 0; int mx=0; for(int i=1;i<=n;i++)mx=max(mx,rst[i]); for(int i = 1; i <= n; i ++){ scanf("%d", &b[i]); if(b[i])q[++num]=P(b[i],w[rst[i]]); } sort(q + 1, q + num + 1); LL best=~0ULL>>1; int pos=1; for(int i=1;i<=num;i++){ pre[i][0]=pre[i-1][0]+1LL*q[i].first*q[i].second; pre[i][1]=pre[i-1][1]+q[i].second; } suf[num+1][0]=suf[num+1][1]=0; for(int i=num;i;i--){ suf[i][0]=suf[i+1][0]+1LL*q[i].first*q[i].second; suf[i][1]=suf[i+1][1]+q[i].second; } for(int i=1;i<=num;i++){ LL now=1LL*q[i].first*pre[i][1]-pre[i][0]; now+=suf[i][0]-1LL*q[i].first*suf[i][1]; if(now<best)best=now,pos=i; } int y=q[pos].first; printf("%d\n", y); fflush(stdout); scanf("%s", s); if(s[0] == 'E'){ return 0; } else{ for(int i = 1; i <= n; i ++){ if(s[0] == '<' && b[i] <= y){ rst[i] --; } else if(s[0] == '>' && b[i] >= y){ rst[i] --; } } } } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
J. Stairways
设$pre[i]=\max(a[1..i])$,则对于任意一个前缀$i$来说,两个子序列必有一个的最大值为$pre[i]$,设$f[i][j]$表示考虑前缀$i$,除了$pre[i]$之外另一个子序列最大值为$j$时的最小代价,考虑如何从$f[i-1][]$转移到$f[i][]$。
若$a[i]=pre[i]$,那么显然将它归到较大一侧最优,故$f[i][j]=f[i-1][j]+a[i]$,只需要把答案加上$a[i]$即可。
否则$a[i]<pre[i]$,那么假设它不作为最大值,则对于$[a[i],pre[i]]$的$j$,有$f[i][j]=f[i-1][j]+j$,对于$[0,a[i])$的$j$,有$f[i][j]=f[i-1][j]+pre[i]$。
而如果它作为最大值,则有$f[i][a[i]]=\min(f[i-1][0..a[i]])+a[i]$。
故需要一个数据结构维护$f[j]$,支持区间加上一个数,区间每个位置$j$加上$j$,以及区间查询最小值,单点修改。
将$f$分块,每块维护凸壳和标记即可,因为查询横坐标递增,故不断将凸壳的队首弹出即可均摊$O(\sqrt{n})$每次查询。
时间复杂度$O(n\sqrt{n})$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=100010,K=330; const ll inf=1000000000000010LL; int n,m,lim,i,a[N],b[N],pre[N],pos[N],st[K],en[K],tx[K],q[N],head[K],tail[K]; ll base,f[N],tb[K]; inline void up(ll&a,ll b){a>b?(a=b):0;} inline double slope(int x,int y){return 1.0*(f[x]-f[y])/(b[y]-b[x]);} inline void build(int x){ int&h=head[x],&t=tail[x],l=st[x],r=en[x]; h=l,t=h-1; for(int i=r;i>=l;q[++t]=i--)while(h<t&&slope(q[t-1],q[t])>slope(q[t],i))t--; } inline void addx(int l){ int L=pos[l]; for(int i=l;i<=en[L];i++)f[i]+=b[i]; for(int i=pos[m];i>L;i--)tx[i]++; } inline void addf(int r,int p){ int R=pos[r]; for(int i=st[R];i<=r;i++)f[i]+=p; build(R); for(int i=0;i<R;i++)tb[i]+=p; } inline ll cal(int x,int y){return f[x]+1LL*y*b[x];} inline ll ask(int x){ int&h=head[x],t=tail[x],o=tx[x]; while(h<t&&cal(q[h],o)>cal(q[h+1],o))h++; return cal(q[h],o); } inline ll query(int r){ ll ret=inf; int R=pos[r]; for(int i=st[R];i<=r;i++)up(ret,f[i]+1LL*tx[R]*b[i]+tb[R]); for(int i=0;i<R;i++)up(ret,ask(i)+tb[i]); return ret; } int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]),base-=a[i],b[i]=a[i]; sort(b+1,b+n+1); for(i=1;i<=n;i++)if(b[i]!=b[i-1])b[++m]=b[i]; for(lim=1;lim*lim<=m;lim++); for(i=1;i<=n;i++)pre[i]=max(pre[i-1],a[i]); for(i=0;i<=m;i++)pos[i]=i/lim; for(i=0;i<=m;i++)en[pos[i]]=i; for(i=m;~i;i--)st[pos[i]]=i; for(i=0;i<=pos[m];i++)build(i); for(i=1;i<=n;i++){ if(pre[i]==a[i]){ base+=a[i]; continue; } int x=lower_bound(b,b+m+1,a[i])-b; ll t=query(x)+a[i]; addx(x); addf(x-1,pre[i]); up(f[x],t-tb[pos[x]]-1LL*tx[pos[x]]*b[x]); build(pos[x]); } printf("%lld",query(m)+base); }
K. Hiding a Tree
将所有可以修改编号的点的编号随机设置,然后再挑选一个影响答案的点修正异或值。
若当前方案不合法,则在有解的情况下是小概率事件,多轮随机即可。
注意生成随机编号的时候要避免与之前编号相同,因为根据生日悖论,在$10^9$内取$10^5$个随机数中有重复元素的概率超过$50\%$,会导致这一轮随机作废。
#include<cstdio> #include<ctime> #include<cstdlib> #include<algorithm> #include<set> using namespace std; const int N=100010; int n,i,a[N],e[N][2],b[N],c[N],q[N],m,v[N]; int used[N]; set<int>T; inline int ask(){ while(1){ int x=rand()%1000000000+1; if(x<N&&used[x])continue; if(T.find(x)!=T.end())continue; T.insert(x); return x; } } void solve(){ T.clear(); for(i=1;i<=n;i++){ if(a[i])b[i]=ask(); else b[i]=i; } int ret=n; for(i=1;i<=n;i++)if(v[i])ret^=b[i]; if(ret){ if(!m)return; int x=rand()%m+1; b[q[x]]^=ret; } for(i=1;i<=n;i++){ if(b[i]<1||b[i]>1000000000)return; c[i]=b[i]; } sort(c+1,c+n+1); for(i=1;i<n;i++)if(c[i]==c[i+1])return; printf("%d\n",n); for(i=1;i<n;i++)printf("%d %d\n",b[e[i][0]],b[e[i][1]]); exit(0); } int main(){ srand(time(NULL)); scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&a[i]); if(!a[i])used[i]=1; } for(i=1;i<n;i++){ scanf("%d%d",&e[i][0],&e[i][1]); v[e[i][0]]^=1; v[e[i][1]]^=1; } for(i=1;i<=n;i++)if(a[i]&&v[i])q[++m]=i; for(int _=100;_;_--)solve(); puts("-1"); }