JZWC【Day1】题解&总结

来源:互联网 发布:台湾驱动透视源码 编辑:程序博客网 时间:2024/04/30 05:07

好吧,就从今年的WinterCamp来写起。
有什么好说的呢,竟然是原题!还是我之前看过题解的原题!竟然不会做!~~蒟蒻呀!

T1 线段树神马的最讨厌了

Description

给出T个询问,每次询问从0~lim选择一个最小的n,使得区间[l,r]出现在以[0,n]为root的线段树中。

Input

第一行输入一个正整数T 表示数据组数。
接下来T 行每行三个整数L;R; lim 表示一组询问,如果对于所有的0 <= n <= lim 都不存在满足条件的解,输出-1 即可。

Output

对于每组询问输出一个答案。

Sample Input

2
0 5 10
6 7 10

Sample Output

5
7

Data Constraint
这里写图片描述

Solution

发现 [l,r] 的父节点代表的区间至多有 4 种,分别为 [l, 2r-l], [l, 2r-l+1],
[2l-r−2, r], [2l−r−1,r],所以只需要暴力枚举这些搜下去就行,加些剪枝就可以过。

Code

#include<cstdio>using namespace std;int ty,n,l,r,ans;void dfs(int l,int r) {    if (r>=ans||r>n) return;    if (l==0) {        ans=r;return;    }    if (r>=l*2) return;    int cnt=r-l+1;    if (l-cnt==0||l-cnt>=cnt+cnt) dfs(l-cnt,r);    if (l-cnt==1||l-cnt-1>=cnt+cnt+1) dfs(l-cnt-1,r);    if (l>=cnt+cnt-1) dfs(l,r+cnt-1);    if (l>=cnt+cnt) dfs(l,r+cnt);}int main() {    for(scanf("%d",&ty);ty;ty--) {        scanf("%d%d%d",&l,&r,&n);ans=n+1;        dfs(l,r);        if (ans>n) printf("-1\n");else printf("%d\n",ans);    }}

T2 已经没有什么好害怕的了

Description

给定一个括号序列,一个合法串是指每个左括号都有与之匹配的右括号,求经过每个字符的合法子串的数量。

Input

第一行输入一个正整数T 表示数据组数。接下来T 行每行一个字符串。

Output

对于每组数据,输出一个整数表示答案,令ansi 为经过第i 个位置的子串个数,那么你需要输出

Sample Input

1
()()

Sample Output

20
样例解释:
ans 数组为{2,2,2,2},所以输出20。

Data Constraint

对于10% 的数据,n<=100
对于30% 的数据,n <= 1000
对于60% 的数据,n <= 5 <= 10^4
对于100% 的数据,n <= 10^6,1 <= T<= 10

Solution

一道神题,%jiry_2大犇
首先用一个栈求出每个点向左最多扩展到的位置,这样我们就得到了一堆线段,做线段覆盖就好了。注意!连在一起的线段要特殊处理。因为两个合法串连在一起还是一个合法串。

Code

#include<cstdio>#include<cstring>#define fd(i,a,b) for(int i=a;i>=b;i--)#define N 1000005#define mo 1000000007#define ll long longusing namespace std;char st[N];int p[N],ty,f[N],bz[N],d[N],tot;ll ans,b[N],c[N],q[N];int main() {    for(scanf("%d",&ty);ty;ty--) {        scanf("%s",st+1);int n=strlen(st+1);ans=0;tot=0;        memset(bz,0,sizeof(bz));memset(f,255,sizeof(f));memset(d,0,sizeof(d));        memset(b,0,sizeof(b));memset(c,0,sizeof(c));memset(q,0,sizeof(q));        fd(i,n,1)             if (st[i]==')') d[++tot]=i;            else if (tot) f[d[tot--]]=i-1;        fd(i,n,1)             if (st[i]==')'&&f[i]!=-1)            {                if (!bz[i]) bz[i]=++tot;                b[i]=++c[bz[i]];                if (f[i]!=-1&&st[f[i]]==')'&&f[f[i]]!=-1) bz[f[i]]=bz[i];               }tot=0;        fd(i,n,1) {            ll k=(c[bz[i]]-b[i]+1)*b[i]%mo;            q[i]=(q[i]+k)%mo;q[f[i]]=(q[f[i]]-k+mo)%mo;           }ll k=0;        fd(i,n,1) {            k=(k+q[i])%mo;ll x=i;            ans=ans+k*x%mo;        }        printf("%lld\n",ans);    }}

T3 我才不是萝莉控呢

Description

求在一个平面直角坐标系中,从(n,1)走到(1,1)的最小花费。走法有两种,
(1) (x,y)~(x-1,y+1) cost 0
(2) (x,y)~(x,(y+1)/2) cost Bx
其中Bi=这里写图片描述

Input

第一行输入一个正整数T 表示数据组数。
对于每组数据,第一行是一个整数n,接下来一行n 个整数表示数组A。

Output

对于每组数据,输出一个整数表示答案。

Sample Input

1
3
1 1 1

Sample Output

5
样例解释:
选择的路径可以是:(3, 1)->(2, 2)->(2, 1)->(1, 2)->(1, 1)

Data Constraint

对于30% 的数据,n <= 10
对于50% 的数据,n <=1000
对于100% 的数据,n<= 10^5,1 <= T<= 10,1 <= Ai<= 10^4

Solution

神奇的模型,其实就是求A数组的哈夫曼树非叶节点权值和。
因为数组是有序的,所以在哈夫曼树中的深度一定是单调不减的。我们考虑每一个位置,把 fi,j 看成现在已经放入了下标比 i 小的所有节点,剩余的未确定值的叶子节点有 j 个。那么我们每一次有两种选择,第一种是把所有叶子节点都扩展出两个后继,付出的代价是 ∑n k=i+1 Ak,状态变成了 fi,2j;第二种是把第 i 个数填在一个叶子上,这
时状态变成了 fi+1,j−1。最终的答案就是 fn+1,0。
把这个DP倒过来就变成了题目中的走法,用经典的堆做就可以了。

Code

#include<cstdio>#include<queue>#define fo(i,a,b) for(int i=a;i<=b;i++)#define ll long long using namespace std;priority_queue<int>q;int ty,n,x,y;ll ans;int main() {    for(scanf("%d",&ty);ty;ty--) {        scanf("%d",&n);ans=0;        fo(i,1,n) {            scanf("%d",&x);q.push(-x);        }        fo(i,1,n-1) {            x=-q.top();q.pop();            y=-q.top();q.pop();            ans=ans+x+y;q.push(-x-y);        }q.pop();        printf("%lld\n",ans);    }}

好吧,我承认这次考跪了。哈夫曼树的模型没看出来…看来还是经验不够呀。时间也没分配好,死磕第二题而没有时间想第三题,可第二题还是爆0了….

明天是Day2,一般来讲会比Day1还要难,希望能不要再跪烂地板了~~~

0 0
原创粉丝点击