数论7.13
7.13 数论练习
P4301 新Nim游戏
思路:
考虑这是 \(Nim\) 游戏,想要先手必胜,就要让对方在第一回合无论拿走几堆后剩下的所有堆异或和都不为 \(0\)
考虑线性基,若插进去的 \(x\) 最后为 \(0\) 就说明 \(x\) 不能插
所以搞一下贪心,将所有石子堆 \(a_i\) 按个数从大到小遍历
能插就插,否则就拿走
简单证一下:如果对于 \(b_1\oplus b_2\oplus \cdots \oplus b_k=x\) 那么根据 \(b_1\oplus b_2\oplus \cdots \oplus b_k\le\sum_{i=1}^k b_i\) 可知拿走 \(x\) 更优
所以让大的先插进去会更优
// Problem: P4301 [CQOI2013] 新Nim游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4301
// Memory Limit: 125 MB
// Time Limit: 1000 ms
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=105;
int n;
int a[N],p[N];
signed main(){
n=read();
int ans=0;
for(int i=1;i<=n;++i) a[i]=read();
sort(a+1,a+n+1);
for(int i=n;i;--i){
int x=a[i];
for(int j=31;j>=0;--j){
if((1<<j)&x){
if(p[j]) x^=p[j];
else{
p[j]=x;
break;
}
}
}
if(!x) ans+=a[i];
}
cout<<ans;
}
P4151 最大XOR和路径
思路:
由于是最大异或和,考虑线性基
显然对于 \(1\) 到 \(n\) 的路径上每一个环都可以选择遍历
分两种情况讨论:
- \(1\) 到 \(n\) 只有一条路径,此时把路径上可遍历的所有的环的异或和扔进线性基,求最大就行
- \(1\) 到 \(n\) 不止一条路径,此时 \(1\) 和 \(n\) 也在一个环内,这样我们可以随机选一条路径,同时把 \(1,n\) 所在的也扔进线性基求最大就行
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
#define ll long long
#define int long long
#define pb push_back
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
struct fake_pair{
ll a,b;
};
int n,m;
bool vis[N];
ll dis[N],p[N];
vector <fake_pair> G[N];
inline void insert(ll x){
for(ll i=63;i>=0;--i)
if(x&(1ll<<i)){
if(!p[i]){
p[i]=x;
return;
}
x^=p[i];
}
}
inline ll query(ll x){
for(ll i=63;i>=0;--i)
if((x^p[i])>x)
x^=p[i];
return x;
}
inline void dfs(ll x,ll sum){
vis[x]=1;
dis[x]=sum;
for(auto y:G[x]){
if(vis[y.a]) insert(sum^y.b^dis[y.a]);
else dfs(y.a,sum^y.b);
}
}
signed main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
ll x=read(),y=read(),z=read();
G[x].pb({y,z});
G[y].pb({x,z});
}
dfs(1,0);
cout<<query(dis[n]);
}
平面最近(远)点对
我们充分发扬人类智慧,把所有点按 \(x,y\) 从小到大排序
根据数学直觉,取后 \(50\) 个点可以求得最近距离
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n;
struct pt{
double x,y;
inline bool operator < (const pt A) const {
return x==A.x?y<A.y:x<A.x;
}
}a[N];
inline double dist(pt A,pt B){
return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}
signed main(){
n=read();
double mn=1000000000000000000;
for(int i=1;i<=n;++i) scanf("%lf %lf",&a[i].x,&a[i].y);
sort(a+1,a+n+1);
for(int i=1;i<=n;++i){
for(int j=i+1;j<=min(i+20,n);++j)
mn=min(mn,dist(a[i],a[j]));
}
printf("%.4lf",sqrt(mn));
}
上面的方法被卡了
我们充分发扬人类智慧,将 \(x,y\) 均加上 \(2333\) 并按 \(x\times y\) 从小到大排序
根据数学直觉,取后 \(900\) 个点可以求得最近距离
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
#define re register
#define int long long
#define nm 2333
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n;
struct pt{
int x,y;
inline bool operator < (const pt A) const {
return (x+nm)*(y+nm)>(A.x+nm)*(A.y+nm);
}
}a[N];
inline int dist(pt A,pt B){
return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}
signed main(){
n=read();
int mn=1000000000000000;
for(int i=1;i<=n;++i) a[i].x=read(),a[i].y=read();
sort(a+1,a+n+1);
for(re int i=1;i<=n;++i){
for(re int j=i+1;j<=min(i+900,n);++j)
mn=min(mn,dist(a[i],a[j]));
}
printf("%lld",mn);
}

浙公网安备 33010602011771号