2021.7.10 Contest 题解
T1:
Description:
Alice 和 Bob 在玩石头剪刀布,他们每个人写出一个序列。 Alice 写出了 (n) 个数, Bob 写出了 (n) 个数。 其中 (0) 代表石头,(1) 代表剪刀,(2) 代表布,(0) 赢 (1) ,(1) 赢 (2) ,(2) 赢 (0) 。
他们总共进行 (k) 轮游戏,第一轮选择第一个数字,后面每一轮两个人都选择序列的下一个数进行比赛(序列结尾的下 一个位置在序列开头)。 一个人的积分为其赢的次数加上额外积分。
额外积分:对于每一个 ([1,k-x+1]) 内的正整数 ,满足某人从第 (i) 轮到第 (i+x-1) 轮都赢,都会让这个人获得 (1) 的额外积分。
问 Alice 和 Bob 每人积分是多少。
Input:
第一行三个数 (n,k,x)。
第二行 (n) 个不大于 (2) 的非负整数。
第三行 (n) 个不大于 (2) 的非负整数。
Output:
一行两个整数表示 Alice 和 Bob 每人积分。
Sample1 Input:
5 10 2
1 1 0 2 0
1 0 2 2 0
Sample1 Output:
0 6
Hint:
对于 (20\%) 的数据,(n,k leq 1000,x=n+1)。
对于另外 (20\%) 的数据,(x=n+1)。
对于另外 (20\%) 的数据,(k leq 500000)。
对于 (100\%) 的数据,(1 leq n leq 500000,1 leq k,x leq 10^{18})
题目分析:
小清新模拟题,先 (O(n)) 预处理出一个循环之后的双方得分,然后随便乱搞即可。
代码如下(马蜂很丑,不喜勿喷)——
#include<bits/stdc++.h>
#define N 500005
#define LL long long
using namespace std;
LL n,ans1,ans2,m,x,a[N],s1[N],s2[N],S1[N],S2[N];
inline int read(){
int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;
}
int main(){
freopen("a.in","r",stdin);freopen("a.out","w",stdout);
cin>>n>>m>>x;for(register int i=1;i<=n;i++) a[i]=read();for(register int i=1,xx;i<=n;i++){
xx=read();if(a[i]==0){if(xx==1) a[i]=1;else if(xx==2) a[i]=-1;else a[i]=0;continue;}
if(a[i]==1){if(xx==2) a[i]=1;else if(xx==0) a[i]=-1;else a[i]=0;continue;}if(xx==0) a[i]=1;else if(xx==1) a[i]=-1;else a[i]=0;
}
for(register int i=1;i<=n;i++){s1[i]=s1[i-1],s2[i]=s2[i-1];if(a[i]==1) s1[i]++;else if(a[i]==-1) s2[i]++;}
LL X=m/n,Y=m%n;ans1=X*s1[n]+s1[Y],ans2=X*s2[n]+s2[Y];if(x>m){cout<<ans1<<' '<<ans2<<'
';return 0;}
if(x>=n){if(s1[n]==n) ans1+=m-x+1;else if(s2[n]==n) ans2+=m-x+1;cout<<ans1<<' '<<ans2<<'
';return 0;}
for(register int i=1;i<=n-x+1;i++){S1[i]=S1[i-1],S2[i]=S2[i-1];if(s1[i+x-1]-s1[i-1]==x) S1[i]++;else if(s2[i+x-1]-s2[i-1]==x) S2[i]++;}
for(register int i=n-x+2;i<=n;i++){S1[i]=S1[i-1],S2[i]=S2[i-1];if(s1[n]-s1[i-1]+s1[i+x-n-1]==x) S1[i]++;else if(s2[n]-s2[i-1]+s2[i+x-n-1]==x) S2[i]++;}
X=(m-x+1)/n,Y=(m-x+1)%n;ans1+=X*S1[n]+S1[Y],ans2+=X*S2[n]+S2[Y];cout<<ans1<<' '<<ans2<<'
';return 0;
}
T2:
Description:
有 (t) 次询问,每次给你一个数 (n) ,求在 ([1,n]) 内约数个数最多的数的约数个数。
Input:
第一行一个正整数 (t) 。
之后 (t) 行,每行一个正整数 (n) 。
Output:
输出 (t) 行,每行一个整数,表示答案。
Sample1 Input:
5
13
9
1
13
16
Sample1 Output:
6
4
1
6
6
Hint:
对于 (100\%) 的数据,(tleq 500,1leq n leq 10^{18})。
题目分析:
注意到要使约数个数尽可能的多,其可用的质因数小于等于41(其实37就够了),跑个爆搜就能过了。
但是作为追求更优解的人,我们肯定不满足于此。我们考虑背包。
设 (f_{i,j}) 表示用到了第 (i) 个质数,当前约数个数为 (j) 所对应的最小的 (n),那么对于每一个 (j) 只需判断 (f_{12,j}) 是否小于 (n) 即可。
代码如下(马蜂很丑,不喜勿喷)——
#include<bits/stdc++.h>
#define N 200005
#define LL long long
using namespace std;
LL n[N],f[20][N],pri[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};int T,tot,ans;LL Max;
inline int read(){
int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;
}
int main(){
// freopen("b.in","r",stdin);freopen("b.out","w",stdout);
T=read();for(register int i=1;i<=T;i++) cin>>n[i],Max=max(Max,n[i]);int up=110000;if(Max<=1e9) up=10000;
f[0][1]=1;for(register int i=1;i<=12;i++) for(register int j=1;j<=up;j++) if(f[i-1][j]){
LL x=f[i-1][j];if(!f[i][j]) f[i][j]=x;else f[i][j]=min(f[i][j],x);
int X=0;while(x<=Max/pri[i]){x*=pri[i],X++;if(!f[i][j*(X+1)]) f[i][j*(X+1)]=x;else f[i][j*(X+1)]=min(f[i][j*(X+1)],x);}
}
for(register int i=1;i<=T;i++){int ans=1;for(register int j=up;j;j--) if(f[12][j]&&f[12][j]<=n[i]){ans=j;break;}cout<<ans<<'
';}return 0;
}
T3:
Description:
给你一个长为 (n) 的序列 (a) 和一个常数 (k) 。
有 (m) 次询问,每次查询一个区间 ([l,r]) 内所有数最少分成多少个连续段,使得每段的和都 (leq k)。
如果这一次查询无解,输出 "Chtholly",输出的字符串不包含引号。
Input:
第一行三个数 (n,m,k)。
第二行 (n) 个数表示这个序列 (a_i) 。
之后 (m) 行,每行给出两个数 (l,r) 表示一次询问。
Output:
输出 (m) 行,每行一个整数,表示答案。
Sample1 Input:
5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4
Sample1 Output:
1
1
1
2
2
Hint:
对于 (100\%) 的数据,满足 (1le n,mle10^6,1le a_i,xle10^9)。
题目分析:
倍增套路题。对于每个 (i) 我们可以二分或者 two-pointers 预处理出最大的 (j) ,满足 (sum_{k=i}^{j}{a_k} leq x)
然后对于每一组询问,从 (l) 开始倍增,直到刚好大于 (r) ,如果没法大于 (r),则无解。
代码如下(马蜂很丑,不喜勿喷)——
#include<bits/stdc++.h>
#define N 1000005
#define LL long long
using namespace std;
int n,m,tot,f[N][24];LL S[N],K;
inline int read(){int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;}
int main(){
freopen("c.in","r",stdin);freopen("c.out","w",stdout);
n=read(),m=read(),K=read();for(register int i=1;i<=n;i++) S[i]=S[i-1]+(LL)read();
for(register int i=1;i<=n;i++){if(S[i]-S[i-1]>K){f[i][0]=i;continue;}int l=i,r=n,x=i;while(l<=r){int mid=l+r>>1;if(S[mid]-S[i-1]<=K) x=mid,l=mid+1;else r=mid-1;}f[i][0]=x+1;}
for(register int x=n;x;x--) for(register int i=1;i<=19;i++) f[x][i]=f[f[x][i-1]][i-1];while(m--)
{int l=read(),r=read(),res=0;for(register int i=19;~i;i--) if(f[l][i]&&f[l][i]<=r) l=f[l][i],res+=(1<<i);if(f[l][0]<=r) puts("Chtholly");else cout<<res+1<<'
'; }return 0;
}
T4:
Description:
给定一棵 (n) 个节点的树,第 (i) 个点的编号为 (i) ,第 (j) 条边的编号为 (j) 。
有 (m) 次查询,每次给出 (l,r) ,查询如果只保留树上点编号在 ([l,r]) 内的点,边编号在 ([l,r]) 内的边,有多少点连通块。
此时点 (a) 与 (b) 连通等价于 (l leq a,b leq r) 且 (a,b) 在树上的简单路径中所有点与边编号都在 ([l,r]) 之间。
Input:
第一行两个数 (n,m) 。
之后 (n-1) 行,编号从 (1) 开始,第 (i) 行三个数 (x,y) 表示编号为 (i) 的边连接着点 (x,y) 。
之后 (m) 行,每行两个数 (l,r) 表示询问区间 ([l,r]) 。
Output:
对每次询问输出一行一个数表示答案。
Sample1 Input:
10 10
1 2
2 3
1 4
1 5
6 4
7 2
8 3
1 9
3 10
1 6
6 7
1 8
3 3
7 10
4 10
8 9
2 3
5 8
5 9
Sample1 Output:
1
2
1
1
4
6
2
1
4
5
Hint:
对于其中 (30\%) 的数据,(n,m leq 10^3).
对于其中 (50\%) 的数据,(nleq 10^3).
对于另外 (20\%) 的数据,(n,mleq 10^5).
对于全部数据,(1 leq n,m leq 10^6).
题目分析:
容易想到,对于一次询问,答案=点数-边数=((r-l+1)-有用的边数).
何为有用的边?即对于边 (i) ,满足 (lle i le r) ,并且 (i) 连接的两个点 (x,y) , (l leq x,y leq r)。
也就是说对于边 (i) 必须满足 $lle min{(i,x,y) le max{(i,x,y)} le r} $。
于是这道题就变成了二维数点问题,离线之后用树状数组维护即可。
代码如下(马蜂很丑,不喜勿喷)——
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n,m,S[N],ans[N];struct node{int l,r;}p[N];struct query{int id,l,r;}q[N];
inline bool cmp(const node x,const node y){return x.l>y.l;} inline bool cmp2(const query x,const query y){return x.l>y.l;}
inline void U(int x){for(register int i=x;i<=n;i+=i&-i) S[i]++;}inline int Q(int x){int y=0;for(register int i=x;i;i-=i&-i) y+=S[i];return y;}
inline int read(){int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;}
int main(){
freopen("d.in","r",stdin);freopen("d.out","w",stdout);
n=read(),m=read();for(register int i=1,x,y;i<n;i++) x=read(),y=read(),(x>y)&&(swap(x,y),0),p[i].l=min(i,x),p[i].r=max(i,y);int now=1;
sort(p+1,p+n,cmp);for(register int i=1;i<=m;i++) q[i].id=i,q[i].l=read(),q[i].r=read();sort(q+1,q+m+1,cmp2);
for(register int i=1;i<=m;i++){int id=q[i].id,L=q[i].l,R=q[i].r;while(now<n&&L<=p[now].l) U(p[now].r),now++;ans[id]=(R-L+1)-Q(R);}
for(register int i=1;i<=m;i++) cout<<ans[i]<<'
';return 0;
}