好吧,就从今年的WinterCamp来写起。
有什么好说的呢,竟然是原题!还是我之前看过题解的原题!竟然不会做!~~蒟蒻呀!
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);
}
}
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 long
using 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);
}
}
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还要难,希望能不要再跪烂地板了~~~