【20170604校内模拟赛】香蕉

学长@FallDream所出的一套模拟赛,难度中等稍难,涵盖范围较广。

T1(香蕉锤)

Description

小Z有\(n\)个香蕉排成了一行。
由于上次比赛小Z没有AK,所以他很不高兴,拿出了他的香蕉锤开始锤香蕉。你已经知道了他锤香蕉的顺序,你想知道,每次他锤的香蕉左右两边第一个还没被锤掉的香蕉是哪一个。

Input

第一行一个数字,表示有\(n\)个香蕉。
接下来\(n\)个数字,第\(i\)个数字表示他第\(i\)次锤的香蕉是哪一个。

Output

输出\(n\)行,每行两个数,表示第\(i\)次锤掉香蕉之后,那个被锤掉的香蕉左右两边第一个还没有被锤掉的香蕉是哪一个?
如果已经没有香蕉了,输出-1

Sample Input

5
2 4 5 3 1

Sample Output

1 3
3 5
3 -1
1 -1
-1 -1

Hint

对于50%的数据,$ n \leq 5*10^3 $

对于100%的数据,$ n \leq 5*10^5 $

Time Limit per Test Case: 1000ms
Memory Limit: 128MB

Solution

裸题,使用双向链表维护即可。
时间效率\(O(n)\)

Code

#include <stdio.h>
#define MN 500005
#define r register
#ifndef Debug
    #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++)
    char BB[1<<15],*S=BB,*TT=BB;
#endif
inline int read(){
    r int x; r bool f; r char c;
    for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
    for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
    return f?-x:x;
}
int n,lst[MN],nxt[MN];
int main(){
    n=read();for (r int i=1; i<=n; ++i) lst[i]=i-1,nxt[i]=i+1;
    lst[1]=-1,nxt[n]=-1;for (r int i=1; i<=n; ++i){
        r int x=read();printf("%d %d\n",lst[x],nxt[x]);
        if (lst[x]!=-1) nxt[lst[x]]=nxt[x];
        if (nxt[x]!=-1) lst[nxt[x]]=lst[x];
    }return 0;
}

T2(香蕉树)

Description

小Z喜欢吃香蕉。
小Z种了一棵由\(n\)个节点组成的香蕉树,每个节点都是一个香蕉,且这些节点被\(n-1\)条树枝连接,保证任意两个香蕉之间有且只有一条路径。每条树枝上都有一个权值。小Z想到了一个问题,他想知道所有(两个香蕉之间的路径的权值异或和)的异或和是多少?

Input

第一行一个整数\(n\),表示这棵香蕉树有\(n\)个节点。
接下来\(n-1\)行,每行3个数\(u,v,w\),描述一条连接香蕉\(u\)和香蕉\(v\)且权值为\(w\)的树枝。

Output

输出一个整数,表示题目所求的答案。

Sample Input

3
1 2 1
2 3 2

Sample Output

0

Hint

对于40%的数据,\(n \leq 300\)

对于60%的数据,$n \leq 2*10^3 $

对于100%的数据,$ n \leq 5*10^5$ .

样例解释

1到2的路径的权值异或和是1;2到3是2;1到3是1^2=3,所以答案是1^2^3=0
Time Limit per Test Case: 1000ms
Memory Limit: 128MB

Solution

显然,一条边对答案的贡献次数为这条边以这条边其中一个端点为根时另一个端点的子树大小相乘。
考虑异或的特殊性,我们有如下性质:

\[a\; xor\; b\; xor\; c=(a\; xor\; b)xor\; c=(a xor\; c)xor\; b\]
\[a\; xor\; a=0,a\; xor\; 0=a\]

因此,我们容易发现一条边对答案有贡献,当且仅当这条边的贡献次数为奇数时,且贡献为这条边的权值。
于是我们就可以得出解题方式,dfs一遍即可。
时间效率\(O(n)\)

Code

#include <stdio.h>
#define MN 500005
#define r register
#ifndef Debug
    #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++)
    char BB[1<<15],*S=BB,*TT=BB;
#endif
inline int read(){
    r int x; r bool f; r char c;
    for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
    for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
    return f?-x:x;
}
int to[MN<<1],nxt[MN<<1],n,sz[MN],cnt,head[MN],val[MN<<1],ans=0;
inline void ins(int x,int y,int v){to[++cnt]=y,nxt[cnt]=head[x],val[cnt]=v;head[x]=cnt;}
void dfs(int u,int f){
    sz[u]=1;for (r int i=head[u]; i; i=nxt[i])
        if (to[i]!=f) dfs(to[i],u),sz[u]+=sz[to[i]],(sz[to[i]]*(n-sz[to[i]]))&1?ans^=val[i]:ans;
}
int main(){
    n=read();for (r int i=1; i<n; ++i){
        r int x=read(),y=read(),val=read();
        ins(x,y,val);ins(y,x,val);
    }dfs(1,0);printf("%d",ans);
}

T3(香蕉川)

Description

小Z来到了香蕉川。
香蕉川由\(n\)座香蕉山组成,第i座山有它的高度\(h_{i}\)。小Z准备从左到右爬这里的恰好\(m\)座香蕉山,但他不希望山的高度起伏太大,太过颠簸,会让本就体育不好的他过于劳累。所以他定义了爬山的劳累度是所有爬的相邻的两座山的高度差的绝对值之和。小Z希望他劳累值最小,所以他想问这个劳累值最小能是多少?

Input

第一行两个整数\(n,m\),表示有\(n\)座山且他准备爬其中恰好\(m\)座山。
第二行个数,分别给出每座山的高度\(h_{i}\)

Output

输出一个整数,表示最小的劳累值。

Sample Input

5 3
1 5 2 3 4

Sample Output

2

Hint

对于50%的数据,\(n \leq 300\)
对于100%的数据,\(n \leq 2000 , 1 \leq h_{i} \leq 1000\)

样例解释

小Z可以选择爬1.2.3三座山或者2.3.4三座山,劳累值为1+1=2
Time Limit per Test Case: 3000ms
Memory Limit: 128MB

Solution

首先我们考虑\(n=300\)的情况下,用\(f_{i,j}\)表示选了j座山,第j座山恰好为i的最优值,显然我们是可以比较容易得到下列一个状态转移方程的:

\[f_{i,j}=min(f_{k:1\rightarrow i-1,j-1}+|h_{k}-h_{i}|) \]

接下来我们考虑优化这个DP,考虑将绝对值拆开,然后利用2棵权值线段树加速即可。
时间效率\(O(n^{2} \log_{2}{n} )\)

Code

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define r register
#define MN 2005
#define M (1<<10) 
#define min(a,b) ((a)<(b)?(a):(b))
#define inf 0x3f3f3f3f
#define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++)
char BB[1<<15],*S=BB,*TT=BB;
inline int read(){
    r int x; r bool f; r char c;
    for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
    for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
    return f?-x:x;
}
int Tmi[M<<1],Tma[M<<1];
int f[MN][MN],a[MN],m,n;
inline int Qhigh(int k){
    r int res=inf;
    for (k+=M-1; k!=1; k>>=1)
        if (~k&1) res=min(res,Tma[k^1]);
    return res;
}
inline int Qlow(int k){
    r int res=inf;
    for (k+=M+1; k!=1; k>>=1)
        if (k&1) res=min(res,Tmi[k^1]);
    return res;
}
inline void A(int k,int val1,int val2){
    Tmi[k+=M]=min(Tmi[k],val1);Tma[k]=min(Tma[k],val2);
    for (k>>=1; k; k>>=1) Tmi[k]=min(Tmi[k<<1],Tmi[k<<1|1]),Tma[k]=min(Tma[k<<1],Tma[k<<1|1]);
}
int main(){
    memset(f,0x3f,sizeof(f));memset(Tmi,0x3f,sizeof(f));
    memset(Tma,0x3f,sizeof(f));n=read(),m=read();
    for (r int i=1; i<=n; ++i) a[i]=read(),f[i][1]=0;
    for (r int i=2; i<=m; ++i){
        for (r int j=1; j<i; ++j) 
            A(a[j],f[j][i-1]-a[j],f[j][i-1]+a[j]);
        for (r int j=i; j<=n; ++j){
            f[j][i]=min(Qhigh(a[j])-a[j],Qlow(a[j])+a[j]);
            A(a[j],f[j][i-1]-a[j],f[j][i-1]+a[j]);
        }
        memset(Tmi,0x3f,sizeof(Tmi));   
        memset(Tma,0x3f,sizeof(Tma));
    }r int ans=inf;
    for (r int i=1; i<=n; ++i)
        ans=min(ans,f[i][m]);
    printf("%d\n",ans);
}

T4(香蕉船)

Description

小Z准备搭建一个香蕉船,因此他需要很多的香蕉。
小Z来到了一个香蕉长廊,第\(i\)个房间有\(a_{i}\)个香蕉。小Z准备选择一些连续的房间并拿走全部的香蕉。出于一些原因,小Z选择的房间数量必须是偶数,并且不小于\(L\)。在此基础上,小Z希望拿到的香蕉的平均数最大,请你告诉他最大的平均数是多少?

Input

第一行有两个整数\(n,L\) ,表示有\(n\)个房间,并且至少选择\(L\)个房间。
接下来一行\(n\)个整数,第\(i\)个整数表示\(a_{i}\)

Output

小Z很讨厌小数。如果答案是个整数,请直接输出。否则,请输出一个最简分数,分子和分母之间用/隔开

Sample Input

5 3
1 1 4 1 1

Sample Output

7/4

Hint

$n \leq 2*10^5,0 \leq a_{i} \leq 10^9 $

40%的数据满足 \(n \leq 5000\)
保证数据有解
Time Limit per Test Case: 2000ms
Memory Limit: 128MB

Solution

二分平均数是多少,并把所有数字减去那么多。
这个二分的值可行,当且仅当存在一段的和大等于0
利用单调栈维护
因为长度必须为偶数,故分奇偶开2个单调栈即可。
时间效率$O(n \log_{2} Ans) $

Code

#include <stdio.h>
#define ld double 
#define ll long long
#define eps (1e-12)
#define MN 200005
#define r register
#define inf 2e9
inline int read(){
    r int x; r bool f; r char c;
    for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
    for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
    return f?-x:x;
}
class que{
    public:
        inline void clear(){h=0;}
        inline int Qpos(){return pos[1];}
        inline ld Qnum(){return h?val[1]:inf;}
        inline void ins(int p,ld v){
            while(h&&v<val[h]) --h;
            pos[++h]=p,val[h]=v;
        }
    private:
        int pos[MN],h;
        ld val[MN];
}Q1,Q2;
ll a[MN];int n,L,ansL,ansR;
inline bool check(ld bar){
    Q1.clear(); Q2.clear();
    for (r int i=L; i<=n; ++i){
        ld nv=1.0*a[i]-bar*i;
        if ((i-L)&1) Q1.ins(i-L,(ld)a[i-L]-bar*(i-L));
        else Q2.ins(i-L,(ld)a[i-L]-bar*(i-L));
        if (nv-((i&1)?Q1.Qnum():Q2.Qnum())>=-eps) return ansL=(i&1?Q1.Qpos():Q2.Qpos()),ansR=i,1;
    }return 0;
}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int main(){
    n=read(),L=read();
    for (r int i=1; i<=n; ++i) a[i]=read()+a[i-1];
    ld L=0,R=inf;
#define mid ((L+R)/2.0)
    for(r int i=1; i<=100; ++i,check(mid)?L=mid:R=mid);
    ll ans=a[ansR]-a[ansL],ans2=ansR-ansL,g=gcd(ans,ans2);
    ans/=g,ans2/=g;if (ans2==1) printf("%lld",ans);
    else printf("%lld/%lld",ans,ans2);return 0;
}

转载于:https://www.cnblogs.com/Melacau/p/6984628.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值