B-小雷的神奇电脑_河南萌新联赛2024第(四)场:河南理工大学 (nowcoder.com)
思路:最大同或对。01字典数模板。
int n,ans=0;
int arr[100005];
int ch[2000006][2]; 开到1e6RE一个点
int idx=0;
void insert(int x){
int p=0;
for(int i=32;i>=0;i--){ ai在int范围内,所以i得是32.
int j=(x>>i)&1;
if(!ch[p][j]) ch[p][j]=++idx;
p=ch[p][j];
}
}
int query(int x){
int p=0,res=0;
for(int i=32;i>=0;i--){ ai在int范围内,所以i得是32.
int j=(x>>i)&1;
if(j&&ch[p][0]){
res+=(1ll<<i);
p=ch[p][0];
}
else if(!j&&ch[p][1]){
res+=(1ll<<i);
p=ch[p][1];
}
else p=ch[p][j];
}
return res;
}
在1e5个数字中选两个,进行异或运算,求可得到的最大异或值---01字典树
P10471 最大异或对The XOR Largest Pair
https://www.luogu.com.cn/problem/P10471
void solve(){ 补B
cin>>n;
for(int i=1;i<=n;i++) cin>>arr[i],insert(arr[i]);
for(int i=1;i<=n;i++) ans=max(ans,query(arr[i]));
cout<<ans;
}
C-岗位分配_河南萌新联赛2024第(四)场:河南理工大学 (nowcoder.com)
思路:
隔板法--太神奇了.. 多余的m-n个人,分配到n个岗位中 因为可以有志愿者空闲下来,那么可以再加一个岗位安放空闲的志愿者。 那么在放好必要的志愿者之后,多余的志愿者可以随意放到n+1个岗位上. 例如样例: 即x1+x2+x3+x4=4 (x>=0) 隔板法要求x>=1,那么可以变形为,(x1+1)+(x2+1)+(x3+1)+(x4+1)=8 这样(x'>=1) 那么按照隔板法,方案数为comb(m-1,n),即在m-1个空位中,放n个板子,得到n+1个不同的岗位安排.
const int mod=998244353;
int n,m;
int quickpow(int a,int b){
int res=1;
while(b){
if(b&1) res*=a,res%=mod;
a*=a,a%=mod;
b>>=1;
}
return res;
}
comb模板
int fac[2003],inv[2003];
void init(int x){
fac[0]=1;
for(int i=1;i<=x;i++) fac[i]=fac[i-1]*i%mod;
inv[x]=quickpow(fac[x],mod-2); 预处理逆元
for(int i=x-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int comb(int x,int y){ x个中选y个
if(y>x) return 0;
if(y==0) return 1;
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
隔板法--太神奇了..
多余的m-n个人,分配到n个岗位中
因为可以有志愿者空闲下来,那么可以再加一个岗位安放空闲的志愿者。
那么在放好必要的志愿者之后,多余的志愿者可以随意放到n+1个岗位上.
例如样例:
即x1+x2+x3+x4=4 (x>=0)
隔板法要求x>=1,那么可以变形为,(x1+1)+(x2+1)+(x3+1)+(x4+1)=8 这样(x'>=1)
那么按照隔板法,方案数为comb(m-1,n),即在m-1个空位中,放n个板子,得到n+1个不同的岗位安排.
岗位分配
https://ac.nowcoder.com/acm/contest/88269/C
void solve(){
cin>>n>>m;
init(2000);
for(int i=1;i<=n;i++){
int x; cin>>x;
m-=x;
}
m+=n+1;
cout<<comb(m-1,n);
}
dp做法:
dp[i][j]定义为,在满足基本条件的前提,考虑前i个岗位,放置j个志愿者的方案数.定义在优化成前缀和后有所不同.
const int mod=998244353;
int n,m;
int dp[2][2003]; dp[i][j]定义为,在满足基本条件的前提,考虑前i个岗位,放置j个志愿者的方案数.优化成前缀和后有所不同
最暴力的是三重循环的,还要枚举前一个放置了几个.这一个循环可以用前缀和给优化了.
岗位分配
https://ac.nowcoder.com/acm/contest/88269/C
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x; cin>>x;
m-=x;
}
dp[0][0]=1;
int ans=0;
for(int i=1;i<=n;i++){
int idx=i&1;
for(int j=0;j<=m;j++){
dp[idx][j]=dp[idx][j-1]+dp[idx^1][j],dp[idx][j]%=mod;这里优化成前缀和,并且优化为滚动数组
// for(int k=0;k<=j;k++){ k=[0,j],这一层优化成前缀和
// dp[i][j]+=dp[i-1][j-k],dp[i][j]%=mod;
// }
if(i==n) ans+=dp[idx][j],ans%=mod; 的确是这样的.
}
}
cout<<ans;
}
K-比赛_河南萌新联赛2024第(四)场:河南理工大学 (nowcoder.com)
思路:枚举裁判,用树状数组维护左边(大于/小于)等于当前裁判的个数,和右边(大于/小于)等于当前裁判的个数。要注意的是,左右边和裁判相等的值是算重了一次的,所以要减去一次左右和裁判相等的值的个数的乘积。
树状数组--单点修改,区间查询(作差)
#define lowbit(x) (x)&-(x)
int n,mx,ans=0;
int arr[20004];
int cL[100005],cR[100005];
void update(int x,int k,int typ){ 0左1右
for(int i=x;i<=mx;i+=lowbit(i)){
typ?cR[i]+=k:cL[i]+=k;
}
}
int query(int x,int typ){
int res=0;
for(int i=x;i;i-=lowbit(i)){
typ?res+=cR[i]:res+=cL[i];
}
return res;
}
比赛
https://ac.nowcoder.com/acm/contest/88269/K
void solve(){ great key:枚举中间的裁判,计算左右满足条件的个数
cin>>n;
mx=0,ans=0; 警钟长鸣:多组样例,清空!!
for(int i=1;i<=n;i++) cin>>arr[i],mx=max(mx,arr[i]);
update(arr[1],1,0); 单点修改
for(int i=3;i<=n;i++) update(arr[i],1,1);
for(int i=2;i<=n-1;i++){
ans+=query(arr[i],0)*(query(mx,1)-query(arr[i]-1,1)); 左小右大
ans+=(query(mx,0)-query(arr[i]-1,0))*query(arr[i],1); 左大右小
ans-=(query(arr[i],0)-query(arr[i]-1,0))*(query(arr[i],1)-query(arr[i]-1,1)); 查询单点值 减去和arr[i]相等的,算重的
update(arr[i],1,0);
if(i+1<=n-1) update(arr[i+1],-1,1);
}
cout<<ans<<endl;
for(int i=1;i<=mx;i++) cL[i]=0,cR[i]=0; 警钟长鸣:多组样例,清空!!
}
E-AND_河南萌新联赛2024第(四)场:河南理工大学 (nowcoder.com)
思路:欧拉筛。容易发现最小的合法区间为[2,3,5],这样只要计算<=y的质数个数,就可以o(1)知道有多少个合法区间.
const int maxn=1e8;
int x,y;
int pri[6000006],idx=0; 前1e8个数字有不到6e6个质数
//vector<int> pri; 动态,静态都行.静态不能开1e8,多余了.
bool mark[maxn+10]; MLE--不要开int mark[]!!之前一直都是开bool的,不知道为什么这次写了int,而且还是long long的
//unordered_map<int,bool> mark; TLE
void getpri(){
for(int i=2;i<=maxn;i++){
if(!mark[i]) pri[++idx]=i;
for(int j=1;j<=idx;j++){
if(i*pri[j]>maxn) break;
mark[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
}
前1e8个数字有不到6e6个质数,怎么找区间?--都不用找,很rz.
don't give up too early.
再思考一会,会发现2这个数字是必要的,因为只有2能提供第0位那个0,其他的质数都是奇数,第0位全是1
并且最小的合法区间为[2,3,5],这样只要计算<=y的质数个数,就可以o(1)知道有多少个合法区间.
AND
https://ac.nowcoder.com/acm/contest/88269/E
void solve(){
cin>>x>>y;
int l=1,r=idx,cnt1=0,cnt2=0;
while(l<=r){ 压行
int mid=(l+r)>>1;
pri[mid]<=y?cnt1=mid,l=mid+1:r=mid-1;
}
cnt2=0,l=1,r=idx;
while(l<=r){
int mid=(l+r)>>1;
pri[mid]<=x-1?cnt2=mid,l=mid+1:r=mid-1;
}
x==2?cout<<cnt1<<" "<<max(0ll,cnt1-2)<<endl:cout<<cnt1-cnt2<<" "<<0<<endl;
}
I-马拉松_河南萌新联赛2024第(四)场:河南理工大学 (nowcoder.com)
思路:dfs,跑出x的合法孩子和y的合法孩子。
int n,x,y;
vector<int> vct[300005];
int dfs(int s,int fa,int typ){
if(s==y&&typ==0) return 0;
if(s==x&&typ==1) return 0;
int res=1;
for(auto v:vct[s]){
if(v!=fa) {
int cur=dfs(v,s,typ);
if(cur==0&&s!=x&&typ==0) return 0;
if(cur==0&&s!=y&&typ==1) return 0;
else res+=cur;
}
}
return res;
}
/马拉松
/https://ac.nowcoder.com/acm/contest/88269/I
void solve(){ I dfs
cin>>n>>x>>y;
for(int i=1;i<=n-1;i++){
int u,v; cin>>u>>v;
vct[u].emplace_back(v);
vct[v].emplace_back(u);
}
cout<<dfs(x,0,0)*dfs(y,0,1);
}
J-尖塔第四强的高手_河南萌新联赛2024第(四)场:河南理工大学 (nowcoder.com)
思路:lca模板。
int n,r,q;
vector<int> vct[100005];
int F[30],idx=3; idx取值为[1,24]
void init(){
F[1]=1,F[2]=2;
while(1){
F[idx]=F[idx-1]+F[idx-2];
if(F[idx]>=100000) break;
idx++;
}
}
int dep[100005],fa[100005][20]; 跳17步足矣
void dfs(int s,int father){
dep[s]=dep[father]+1;
fa[s][0]=father;
for(int i=1;i<=17;i++) fa[s][i]=fa[fa[s][i-1]][i-1];
for(auto v:vct[s]) if(v!=father) dfs(v,s);
}
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=17;i>=0;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=17;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
尖塔第四强的高手
https://ac.nowcoder.com/acm/contest/88269/J
void solve(){ J 手搓lca
cin>>n>>r>>q;
init();
for(int i=1;i<=n-1;i++){
int u,v; cin>>u>>v;
vct[u].emplace_back(v);
vct[v].emplace_back(u);
}
dfs(r,0);
while(q--){
int x,k; cin>>x>>k;
vector<int> test;
for(int i=k;i<=24;i++){
int num=F[i]+x;
if(num>n) break;
test.emplace_back(num);
}
if(test.size()==0) cout<<"0"<<endl;
else{
int cur=test[0];
for(int i=1;i<test.size();i++) cur=lca(cur,test[i]);
cout<<cur<<endl;
}
}
}