• 义乌集训7.10 contest 3题解


    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;
    }
    
  • 相关阅读:
    poj3041(最小顶点覆盖)
    High-speed Charting Control--MFC绘制图表(折线图、饼图、柱形图)控件
    hdu 3183 A Magic Lamp(RMQ)
    Android studio 中创建AIDL Service
    cocos2d-x 3.0正式版 cmd创建project以及一键创建project
    【Machine Learning】决策树案例:基于python的商品购买能力预测系统
    【Machine Learning】机器学习及其基础概念简介
    【Machine Learning】Python开发工具:Anaconda+Sublime
    【HanLP】HanLP中文自然语言处理工具实例演练
    【HanLP】资料链接汇总
  • 原文地址:https://www.cnblogs.com/jiangxuancheng/p/15043828.html
Copyright © 2020-2023  润新知