我这么写会不会侵权...毕竟这是收钱的比赛
F.拿物品
题解
一开始理解错了,以为每个人只是取当前最大的就行,后来发现不对,我取了这个物品,获得\(a_i\) ,不取走获得\(-b_i\) ,所以就按照\(a+b\) 排序然后按大小选就行
#include<iostream> #include<algorithm> #include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<queue> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef pair<int,int> PII; #define X first #define Y second inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=200010; int n,a[maxn],b[maxn],has[maxn],A[maxn],B[maxn],la,lb; PII c[maxn]; int main() { n=read(); for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++)b[i]=read(),c[i]=make_pair(a[i]+b[i],i); sort(c+1,c+n+1); for(int i=1;i<=n;i++) { if(i%2)A[++la]=c[n-i+1].Y; else B[++lb]=c[n-i+1].Y; } for(int i=1;i<la;i++)printf("%d ",A[i]); printf("%d\n",A[la]); for(int i=1;i<lb;i++)printf("%d ",B[i]); printf("%d\n",B[lb]); return 0; }
I.建通道
题解
这场比赛最有价值的一道,题意是建一棵两两边权分别为\(lowbit(v_i⊕v_j)\) 的最小生成树
根据套路,肯定先上\(Trie\) ,我们发现两点边权就是他们在\(Trie\) 树上的LCA所对应的\(lowbit\) ,为了使得最小,这个LCA肯定越小越好。
我们求出这一坨数的总LCA,(这里的LCA就对应着这些二进数的逆序的LCP)(这个LCA显然把所有数划分成了两个集合(一左一右) 我们一定用通过一种连边方式使得这个二分图联通方式是一棵树,所以答案就是\((n-1)*lowbit(LCA)\) .
一定要注意去重。(忘记去重的我当时怀疑人生)
听说CF上还有一个异或最小生成树,明天做做去。
#include<iostream> #include<algorithm> #include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<queue> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef pair<int,int> PII; #define X first #define Y second inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=200010; int n,v[maxn],m; int lca(int u,int v) { int res=0,t=u; while(u && v) { if((u&1)==(v&1))res++; else return (t&((1<<res)-1)); u>>=1;v>>=1; } return (t&((1<<res)-1)); } int main() { n=read(); for(int i=0;i<n;i++)v[i]=read(); sort(v,v+n); n=unique(v,v+n)-v; int now=v[0]; for(int i=1;i<n;i++)now=lca(now,v[i]); int len=0; while(now)len++,now>>=1; printf("%lld\n",(LL)(n-1)*(LL)(1<<len)); return 0; }
J.求函数
题解
看着就像线段树,推一推式子发现满足区间加法,那就直接上了。
#include<bits/stdc++.h> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef pair<LL,LL> PII; #define X first #define Y second inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int MOD=1000000007,maxn=200010; int n,m,A[maxn],B[maxn],kk,bb,tp,x; LL k[maxn<<2],b[maxn<<2]; void maintain(int L,int R,int o) { int mid=L+R>>1,lo=o<<1,ro=lo|1; k[o]=(k[lo]*k[ro])%MOD; b[o]=(k[ro]*b[lo]+b[ro])%MOD; } void build(int L,int R,int o) { if(L==R){ k[o]=A[L];b[o]=B[L]; return; } int mid=L+R>>1,lo=o<<1,ro=lo|1; build(L,mid,lo);build(mid+1,R,ro); maintain(L,R,o); } void update(int L,int R,int o,int x) { if(L==R){ k[o]=kk;b[o]=bb; return; } int mid=L+R>>1,lo=o<<1,ro=lo|1; if(x<=mid)update(L,mid,lo,x); else update(mid+1,R,ro,x); maintain(L,R,o); } PII query(int L,int R,int o,int ql,int qr) { if(L==ql && R==qr)return make_pair(k[o],b[o]); int mid=L+R>>1,lo=o<<1,ro=lo|1; if(qr<=mid)return query(L,mid,lo,ql,qr); else if(ql>mid)return query(mid+1,R,ro,ql,qr); PII t1=query(L,mid,lo,ql,mid),t2=query(mid+1,R,ro,mid+1,qr); return make_pair((t1.X*t2.X)%MOD,(t2.X*t1.Y+t2.Y)%MOD); } int main() { n=read();m=read(); for(int i=1;i<=n;i++)A[i]=read(); for(int i=1;i<=n;i++)B[i]=read(); build(1,n,1); while(m--) { tp=read(); if(tp==1) { x=read();kk=read();bb=read(); update(1,n,1,x); } else { kk=read();bb=read(); PII t=query(1,n,1,kk,bb); printf("%lld\n",(t.X+t.Y)%MOD); } } return 0; }
废话
老子以后干什么都开long long[微笑]
来源:https://www.cnblogs.com/FYH-SSGSS/p/12271682.html