我这么写会不会侵权...毕竟这是收钱的比赛
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