2021杭电多校补题——第一场
文章目录
Mod, Or and Everything
题意:
T
T
T 组测试数据,每组给出一个整数
n
n
n ,求
(
n
m
o
d
n
)
∣
(
n
m
o
d
(
n
−
1
)
∣
(
n
m
o
d
(
n
−
2
)
)
.
.
.
∣
(
n
m
o
d
2
)
∣
(
n
m
o
d
1
)
)
(n\ mod\ n) \ |\ (n\ mod\ (n-1)\ | \ (n\ mod\ (n-2))...\ |\ (n\ mod\ 2)\ |\ (n\ mod\ 1))
(n mod n) ∣ (n mod (n−1) ∣ (n mod (n−2))... ∣ (n mod 2) ∣ (n mod 1)),其中"
∣
|
∣ "表示位运算或
题解:
可以发现从从
n
%
n
n\%n
n%n 开始,取模结果依次为
0
、
1
、
2
、
3...
、
⌊
n
−
1
2
⌋
、
.
.
.
0
0、1、2、3...、\lfloor\frac {n-1} {2}\rfloor、...0
0、1、2、3...、⌊2n−1⌋、...0,即包含了
0
0
0~
⌊
n
−
1
2
⌋
\lfloor\frac {n-1} {2}\rfloor
⌊2n−1⌋ 所有数,由此可推得答案为
2
k
−
1
2^k-1
2k−1 ,其中
2
k
2^k
2k 是第一个大于
⌊
n
−
1
2
⌋
\lfloor\frac {n-1} {2}\rfloor
⌊2n−1⌋ 的二次幂
#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#define int long long
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
const int mod = 1e9+7;
using namespace std;
inline int read(){
char c=getchar();int x=0,s=1;
while(c<'0'||c>'9'){if(c=='-')s=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*s;
}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int N = 1e6+10;
const int maxn = 1e3+10;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--){
int n; cin>>n;
n=(n-1)/2;
int ans=1;
while(ans<=n)ans<<=1;
cout<<ans-1<<endl;
}
}
Rocket land(待补)
.
.
.
Puzzle loop(待补)
.
.
.
Another thief in a Shop(待补)
.
.
.
Minimum spanning tree
题意:
T
T
T 组测试数据,每组给出一个整数
n
n
n 表示图中有
n
−
1
n-1
n−1 个点,每个点的权值分别为
2
2
2 ~
n
n
n ,在顶点
a
a
a 和顶点
b
b
b 之间连一条边的代价是
l
c
m
(
a
,
b
)
lcm(a,b)
lcm(a,b) ,计算将所有点连成一颗树的最小代价
题解:
简单贪心,每个合数顶点都连在自己的最小质因数上,质数顶点连在顶点
2
2
2 上就行了
#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#define int long long
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
const int mod = 1e9+7;
using namespace std;
inline int read(){
char c=getchar();int x=0,s=1;
while(c<'0'||c>'9'){if(c=='-')s=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*s;
}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int N = 1e7+10;
const int maxn = 1e3+10;
bool vis[N];
int prime[N],cnt;
int ans[N];
void get_prime(){
vis[1]=1;
for(int i=2;i<=N;++i){
if(vis[i]==0) {
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&prime[j]*i<=N;++j) {
vis[i*prime[j]]=true;
if(i%prime[j]==0)
break;
}
}
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
get_prime();
for(int i=3;i<=N;i++){
if(vis[i]==0)ans[i]=ans[i-1]+i*2;//质数结点连在顶点2上,代价为2*i
else ans[i]=ans[i-1]+i;//合数结点连在最小质因数上,代价为i
}
int t; cin>>t;
while(t--){
int n; cin>>n;
cout<<ans[n]<<endl;
}
}
Xor sum
题意:
T
T
T 组测试数据,每组第一行给出两个整数
n
、
k
n、k
n、k,接下来一行
n
n
n 个整数,求
L
,
R
L,R
L,R 满足
a
[
L
]
⨁
a
[
L
+
1
]
.
.
.
⨁
a
[
R
]
≤
k
a[L]\bigoplus a[L+1]...\bigoplus a[R]\le k
a[L]⨁a[L+1]...⨁a[R]≤k,若答案有多个则输出左端点最小的
L
R
L\ R
L R,若没有答案则输出
−
1
-1
−1
题解:
这题想了好久了,一直没想明白为什么要用字典树,好家伙原来是用一个数组(pos)来维护字典树每个节点的对应前缀异或和数组(pre)的最右端点(可能有点绕),然后枚举右端点,在字典树上找到一条合法路径使其和
p
r
e
[
i
]
pre[i]
pre[i]异或结果
≤
k
\le k
≤k,代码注释应该说的挺清楚了
#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
const int mod = 1e9+7;
using namespace std;
inline int read(){
char c=getchar();int x=0,s=1;
while(c<'0'||c>'9'){if(c=='-')s=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*s;
}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int N = 3e6+10;
const int maxn = 1e3+10;
int a[N],pre[N];
int trie[N][2],cnt,pos[N];
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--){
int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),pre[i]=pre[i-1]^a[i];//pre存前缀异或和
int ansl=-1,ansr=n,cnt=1;
pos[1]=-1;
trie[1][0]=trie[1][1]=0;
for(int i=0;i<=n;i++){//要从i=0开始,因为pre[0]=0也要插进字典树
int res=-1,fa=1;
for(int j=30;j>=0;j--){
int w=(pre[i]>>j)&1;
if(!((k>>j)&1)){
//如果k>>j位上是0,那 w和w^1 都可以取,因为与 pre[i]>>j异或后 一定 >=0
if(trie[fa][w^1])res=max(res,pos[trie[fa][w^1]]);
//因为 w 和 w^1 都能取,那就取更优解
fa=trie[fa][w];
}
else fa=trie[fa][w^1];
//如果k>>j位上是1,那就只能从w的异或值往下继续找,因为 w^1异或pre[j]>>j 才能等于 1
if(!fa)break;
}
if(fa)res=max(res,pos[fa]);//找到结尾如果fa>0说明该路径合法,res取更优解
if(res>=0&&i-res<ansr-ansl)ansl=res,ansr=i;
fa=1;
for(int j=30;j>=0;j--){//插入字典树
int w=(pre[i]>>j)&1;
if(!trie[fa][w]){
trie[fa][w]=++cnt;
pos[cnt]=-1;
trie[cnt][0]=trie[cnt][1]=0;
}
fa=trie[fa][w];
pos[fa]=max(pos[fa],i);
//维护该节点对应的最右端点
}
}
if(ansl!=-1)cout<<ansl+1<<" "<<ansr<<endl;
else cout<<-1<<endl;
}
}
Pass!(待补)
.
.
.
Maximal submatrix(待补)
.
.
.
KD-Graph
题意:
T
T
T 组测试数据,每组给出三个整数
n
、
m
、
k
n、m、k
n、m、k 表示无向图
G
<
n
,
m
>
G<n,m>
G<n,m>,接下来
m
m
m 行,每行三个整数
u
、
v
、
w
u、v、w
u、v、w
表示顶点
u
u
u 和顶点
v
v
v 之间有一条权值为
w
w
w 的无向边。计算是否存在一个整数
D
D
D 使
n
n
n 个顶点分为
k
k
k 组 满足:
①对于任意一组,组内的顶点两两之间至少存在一条权值
≤
w
\le w
≤w的路径
②对于任意两组,组间的顶点两两之间不存在任何一条权值
≤
w
\le w
≤w的路径
若
D
D
D 存在输出
D
D
D ,否则输出
−
1
-1
−1
题解:
用一个变量
n
o
w
now
now 记录当前组数,将边按权值从小到大排序,每一阶段取出同权值的所有边,将这些边的顶点用并查集两两合并,每进行一次合并
n
o
w
−
1
now-1
now−1,因为合并了一次剩下的顶点数就少了一个 ,并记录权值
w
w
w ,当进行到下一阶段,即边权于与上一次不一样时,判断
n
o
w
now
now的值,若等于k,则
w
w
w 就是答案
#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
const int mod = 1e9+7;
using namespace std;
inline int read(){
char c=getchar();int x=0,s=1;
while(c<'0'||c>'9'){if(c=='-')s=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*s;
}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int N = 1e6+10;
const int maxn = 1e3+10;
struct edge{
int u,v,w;
}e[N];
int f[N];
bool cmp(edge a,edge b){return a.w<b.w;}
inline int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; cin>>t;
while(t--){
int n,m,k; scanf("%d%d%d",&n,&m,&k);//要用scanf不然会超时
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e+1,e+1+m,cmp);
int now=n,ans=0,flag=0;//初始时所有点都是独立的,所以now=n
for(int i=1;i<=m;i++){
if(e[i].w!=e[i-1].w)
if(now==k){ printf("%d\n",ans);flag=1;break; }
/*now==k的话,之前合并的算作一组,因为权值都<=ans
剩下的k-1个点权值都>ans,每个点都是独立的一组
*/
if(find(e[i].u)==find(e[i].v)) continue;
now--; //合并一次独立的顶点就少了一个
f[find(e[i].u)]=find(e[i].v);
ans=e[i].w;
}
if(!flag)printf("%d\n",now==k?ans:-1);
}
}
zoto
题意:
T
T
T 组测试数据,每组两个整数
n
、
q
n、q
n、q,接下来一行
n
n
n 个整数表示数组
f
x
fx
fx, 平面中有
n
n
n 个点,其中第
i
i
i 个的坐标为
(
i
,
f
x
[
i
]
)
(i,fx[i])
(i,fx[i]),接下来
q
q
q 次询问,每次包含四个整数
x
1
、
y
1
、
x
2
、
y
2
x1、y1、x2、y2
x1、y1、x2、y2,对于每次询问,输出一个整数表示在以
(
x
1
,
y
1
)
(x1,y1)
(x1,y1)为左下角,
(
x
2
,
y
2
)
(x2,y2)
(x2,y2)为右上角的矩形中,有多少个
Y
Y
Y 坐标不同的点
题解:
由于只有询问操作,自然想到用莫队来离线处理
X
X
X 轴的移动,关键在于
Y
Y
Y 点数的维护,一开始考虑用线段树,每次
a
d
d
add
add 或者
d
e
l
e
t
e
delete
delete 一个新点就在线段树上更新,然后区间查询,没想到线段树常数太大超时了。于是考虑对
Y
Y
Y 轴的区间分块处理(好像树状数组也能写,下次再来补)
#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
const int mod = 1e9+7;
using namespace std;
inline int read(){
char c=getchar();int x=0,s=1;
while(c<'0'||c>'9'){if(c=='-')s=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*s;
}
void inar(int a[],int n){for(int i=1;i<=n;i++)cin>>a[i];}
void outar(int a[],int n){for(int i=1;i<=n;i++)cout<<a[i]<<" ";cout<<endl;}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
const int maxn = 1e3+10;
int a[N],pos[N],ans[N],cnt[N],sum[N],block=313;
struct node{
int l,r,ma,mi,id;
}query[N];
bool cmp(node a,node b){//莫队优化排序
if(a.l/block!=b.l/block)return a.l/block<b.l/block;
if((a.l/block)&1)return a.r>b.r;
return a.r<b.r;
}
void add(int x){if(++cnt[x]==1)sum[x/block]++;}//找到新点就在对应的块上+1
void del(int x){if(--cnt[x]==0)sum[x/block]--;}//相反-1
int cal(int x){//分块的区间查询
int res=0;
for(int i=0;i<x/block;i++)res+=sum[i];
for(int i=(x/block)*block;i<=x;i++)res+=(cnt[i]>=1);
return res;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t; scanf("%d",&t);//scanf不然会超时
while(t--){
memset(sum,0,sizeof sum); memset(cnt,0,sizeof cnt);
int n,q;scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=q;i++){
scanf("%d%d%d%d",&query[i].l,&query[i].mi,&query[i].r,&query[i].ma);
query[i].id=i;
}
sort(query+1,query+1+q,cmp);
int pl=query[1].l,pr=query[1].r;
for(int i=pl;i<=pr;i++)add(a[i]);
ans[query[1].id]=cal(query[1].ma)-cal(query[1].mi-1);
for(int i=2;i<=q;i++){
while(pl<query[i].l) del(a[pl]),pl++;
while(pr>query[i].r) del(a[pr]),pr--;
while(pl>query[i].l) pl--,add(a[pl]);
while(pr<query[i].r) pr++,add(a[pr]);
ans[query[i].id]=cal(query[i].ma)-cal(query[i].mi-1);
}
for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
}
}