2025多校冲刺CSP模拟赛7
2025多校冲刺CSP模拟赛7
不是你怎么还有加赛?ys
A. gcd&xor (gcdxor)
转化为外层枚举 \(\gcd\),内层枚举 \(i,j\),打表即可。可以发现规律,是调和级数做法,时间复杂度大约 \(O(1.5 \times 10^8)\)。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif
// int logn[1<<20];
// vector<pair<int,int> > v[20][1<<16];
signed main()
{
// #ifndef ONLINE_JUDGE
freopen("gcdxor.in","r",stdin);
freopen("gcdxor.out","w",stdout);
int n;
int ans=0;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=i;j<=n-i;j+=i)
ans+=(j^(j+i))==i;
cout<<ans;
// for(int i=2;i<(1<<20);i++) logn[i]=logn[i>>1]+1;
// for(int k=1;;k<<=1)
// {
// int L=1<<logn[k],R=min(n,(1ll<<(logn[k]+1))-1);
// if(L>n) break;
// cerr<<L<<" "<<R<<"\n";
// for(int i=L;i<=R;i++)
// for(int j=i+1;j<=R;j++)
// {
// if(__gcd(i,j)==(i^j))
// {
// v[logn[k]][i^j].push_back(make_pair(i,j));
// // cout<<i<<" "<<j<<" "<<(i^j)<<"\n";
// ans++;
// }
// }
// // cerr<<"\n------------------\n\n";
// }
// for(int i=1;i<=n;i++)
// {
// bool f=0;
// for(int k=1;k<=logn[n];k++)
// {
// if(v[k][i].empty()) continue;
// f=1;
// cout<<"\nxor="<<i<<"\nk="<<k<<"\n";
// for(auto nw:v[k][i]) cout<<nw.first<<","<<nw.second<<"\n";
// }
// if(f) cout<<"\n-----------------------------------------\n";
// }
// cout<<ans;
// #endif
//mt19937_64 myrand(time(0));
return 0;
}
B. 异或树 (xortree)
暴力写假两次,std 写假一次。
发现规律。加一次点后,只有加操作之前的那些叶子节点的子树 xor 值会更改。非叶子节点的子树 xor 值均固定。
又发现值域很小。所以直接暴力 dp 更改即可。复杂度小常数 \(O(nV)\),可以通过。
注意:有人 (HS_fu3) 使用 unordered_map 被卡常了。
有人:\(2^{13} = 2\)^\({13}\)
《int maxn=2^13》
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif
int k0,q;
struct Node{
int op,x;
}a[1<<20];
vector<int> E[1<<20];
int val[1<<20];
int cnt[8193];
int sum[1<<22];
int tot=1;
void dfs(int p,int x)
{
// cerr<<p<<" "<<x<<" "<<E[p].size()<<"\n";
// exit(0);
if(!E[p].size())
{
E[p].push_back(++tot);
E[p].push_back(++tot);
val[tot-1]=val[p];
val[tot]=val[p]^x;
return;
}
dfs(E[p][0],x);
dfs(E[p][1],x);
}
void find(int p)
{
sum[p]=val[p];
for(int to:E[p])
{
find(to);
sum[p]^=sum[to];
}
cnt[sum[p]]++;
}
signed main()
{
//xortree
// #ifndef ONLINE_JUDGE
freopen("xortree.in","r",stdin);
freopen("baoli.out","w",stdout);
k0=read();
q=read();
val[1]=k0;
cnt[k0]=1;
for(int i=1;i<=q;i++)
{
int op=read(),x=read();
a[i]={op,x};
if(op==1)
{
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
dfs(1,x);
find(1);
}
else
{
// dd(1);
cout<<cnt[x]<<"\n";
}
}
// #endif
//mt19937_64 myrand(time(0));
return 0;
}
C. 符文石 (stone)
困难题。
考虑一个数集支持插入一个数查询选两个数按位与的最大值,怎么维护。
假设当前的两两按位与最大值是 M。显然 \(<M\) 的数都没必要保留,不可能出现在未来任何时刻的最优解里,因为按位与结果不会比运算数更大。
\(=M\) 的数最多保留两个。
对于 \(>M\) 的数 \(a_i\),考虑它和 \(M\) 的二进制最长公共前缀,设 \(b_i\) 是 \(a_i\) 和 \(M\) 不相等的最高二进制位,
只能是 \(M\) 为 \(0\) 而 \(b_i\) 为 \(1\)。
发现最多只有 \(w\) 个数满足未来加入某些新数后这个数有可能变成最大按位与的两个数之一。
只需要记录这些数。(\(w\) 表示二进制位数的范围)
现在我们在图上考虑,不妨直接 DP,\(f_i\) 记录 \(i\) 可达的所有点合并出来的可能有用的 \(w\) 个数。直
接插入维护的时间复杂度是 \(O(w^2)\) 的 DP。(合并两组数时,枚举数对更新 \(f_i\),然后再清除掉所有
\(<M\) 的数以及多余的 \(=M\) 的数即可)
还有个小问题是 DAG 同一个点通过不同路径的重复贡献导致一个数自己和自己取按位与怎么办。
这只需要记录一下数的出现次数即可,只出现一次的数记录来源点标号,两次以上的记为两次以上,就可以
判重。
因为相等的数出现两次和出现两次以上对这个问题没区别。
时间复杂度 \(O(nw^2)\) 可以通过。(枚举两个数求按位与最大,常数很小常数极大无比,但可以通过)。
题解里说要建反图然后跑拓扑排序。
哥们觉得拓扑太麻烦了,所以不采用这种方法。
实现比较简单,直接在原图上 dfs 然后合并,对每个节点打一个 vis 标记保证每个点只会被访问一次,即可通过本题。
复杂度有保证 \(O((n+m)w^2)\),分为计算每个点贡献 \(O(nw^2)\) 和合并数组大常数 \(O(mw^2)\) 两个部分。可以在 \(2300 ms\) 内通过本题。
由于哥们太菜了,不会写合并函数,所以合并函数里写的乱七八糟,不建议食用。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=5e5+5;
int n,m;
int a[N];
vector<int> E[N];
struct Node{
signed val,pos;
};
vector<Node> v[N];
int ans[N];
int toval[N];
bool vis[N];
int cnt[N];
int pos[N];
vector<Node> nw1,nw2;
int findMax()
{
int ans=-1;
for(int i=0;i<v[0].size();i++)
for(int j=i+1;j<v[0].size();j++)
ans=max(ans,toval[v[0][i].val]&toval[v[0][j].val]);
for(const Node &i:v[0])
if(i.pos==-1) ans=max(ans,toval[i.val]);
return ans;
}
void merge(int x,int y,int toid)
{
nw1.reserve(122);
nw2.reserve(122);
for(const Node &i:v[x]) cnt[i.val]=0,pos[i.val]=-1;
for(const Node &i:v[y]) cnt[i.val]=0,pos[i.val]=-1;
for(const Node &i:v[x])
{
if(i.pos!=-1) pos[i.val]=i.pos,cnt[i.val]=1;
else pos[i.val]=-1,cnt[i.val]=2;
}
for(const Node &i:v[y])
{
if(i.pos!=-1&&pos[i.val]==i.pos) continue;
cnt[i.val]+=1+(i.pos==-1);
}
for(const Node &i:v[x])
if(cnt[i.val]==1) nw1.push_back(i),cnt[i.val]=-1;
else if(cnt[i.val]>1) nw1.push_back({i.val,-1}),cnt[i.val]=-1;
for(const Node &i:v[y])
if(cnt[i.val]==1) nw1.push_back(i),cnt[i.val]=-1;
else if(cnt[i.val]>1) nw1.push_back({i.val,-1}),cnt[i.val]=-1;
swap(v[0],nw1);
int maxn=findMax();
swap(v[0],nw1);
for(const Node &i:nw1) if(toval[i.val]>=maxn) nw2.push_back(i);
swap(v[toid],nw2);
nw1.clear();
nw2.clear();
}
void dfs(int p)
{
if(vis[p]) return;
vis[p]=1;
v[p].push_back({(signed)a[p],(signed)p});
for(const int &to:E[p])
dfs(to);
for(const int &to:E[p]) merge(p,to,p);
swap(v[p],v[0]);
ans[p]=findMax();
swap(v[p],v[0]);
}
int Lsh[N]={},tot=0;
unordered_map<int,int> mp;
void lsh(int *val)
{
for(int i=1;i<=n;i++) Lsh[i]=*(val+i);
sort(Lsh+1,Lsh+1+n);
Lsh[0]=-1;
for(int i=1;i<=n;i++)
if(Lsh[i]!=Lsh[i-1]) ++tot,mp[Lsh[i]]=tot,toval[tot]=Lsh[i];
for(int i=1;i<=n;i++) *(val+i)=mp[*(val+i)];
}
signed main()
{
// #ifndef ONLINE_JUDGE
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
n=read();
m=read();
for(int i=1;i<=n;i++) a[i]=read(),v[i].reserve(1);
lsh(a);
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
E[u].push_back(v);
// E[v].push_back(u);
// in[v]++;
// out[u]++;
}
// int root=0;
for(int i=1;i<=n;i++)
if(!vis[i]) dfs(i);
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
// #endif
//mt19937_64 myrand(time(0));
return 0;
}
D. 彩色括号 (witch)
场上拿了 15 pts 部分分跑路了。
Code:
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/p/19157934
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号