2016.11.4
晚上六点点开了一场看上去通过人数比较多的hihoround(20),virtual participate了一下。感觉这场还是比较友好的,和我以前做的hihoround只能做一题,或者爆零不一样。在两个小时里写了3个题(A,B,C),D看上去像是个经典问题,但从来没写过(几何基本等于残疾),便直接放弃了。
这场是岛娘出的,我还以为是ta朋友出的。
感觉题挺不错。题解写的也不错。
官方题解:http://hihocoder.com/discuss/question/3329
我讲讲我的做法吧。
A:入门的区间最大值了,可能是岛娘觉得像CF一样出个很水的没代码量的A没意思,就让大家写个线段树或者ST了。
我的代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 5;
#define lson o * 2,l,mid
#define rson o * 2 + 1,mid + 1,r
struct segmentTree{
int maxv[maxn << 2];
void pushup(int o){
maxv[o] = max(maxv[o << 1],maxv[o << 1 | 1]);
}
void build(int o,int l,int r){
if(l == r) {maxv[o] = 0;return;}
int mid = (l + r) >> 1;
build(lson);
build(rson);
pushup(o);
}
void update(int o,int l,int r,int p,int val){
if(l == r) {maxv[o] = max(maxv[o],val);return;}
int mid = (l + r) >> 1;
if(p <= mid) update(lson,p,val);
else update(rson,p,val);
pushup(o);
}
int query(int o,int l,int r,int ql,int qr){
if(ql <= l && r <= qr){
return maxv[o];
}
int ans = 0;
int mid = (l + r) >> 1;
if(ql <= mid){
ans = max(ans,query(lson,ql,qr));
}
if(qr > mid){
ans = max(ans,query(rson,ql,qr));
}
return ans;
}
}tree;
int n,m;
int main(){
cin >> n >> m;
tree.build(1,1,maxn - 5);
for(int i = 1;i <= n;i++){
int t,v;
scanf("%d%d",&t,&v);
tree.update(1,1,maxn - 5,t,v);
}
int ans;
for(int i = 1;i <= m;i++){
int a,b;scanf("%d%d",&a,&b);
ans = tree.query(1,1,maxn - 5,a,b);
if(ans == 0) printf("None\n");
else printf("%d\n",ans);
}
return 0;
}
B:
这是一个括号匹配类型的问题。仔细分析发现,其实可以是个dp。
dp[i]=dp[left]+1,left是使得s[left+1]−s[i]形成一个合法括号序列的最靠近i的位置,那么ans=∑dp[i],这样的话是个O(n2)的dp,再仔细观察可以发现,其实left就是同层的上一个位置,而且depth从高减的时候,要把上一层pre的删掉。(depth指的是比如((()())),内层括号就是第三层,外面就是第二层,第一层.这样就是线性的了)具体见代码。
代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
const int maxn = 1e6 + 5;
int dp[maxn];
int num[maxn];
char s[maxn];
char Stack[maxn];
int top;
int main(){
scanf("%s",s + 1);
int n = strlen(s + 1);
top = 0;
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
for(int i = 1;i <= n;i++){
if(s[i] == '('){//左括号压栈
dp[i] = 0;
Stack[++top] = '(';
}else{
if(Stack[top] == ')' || top == 0){
dp[i] = 0;
Stack[++top] = ')';
}else{//关键,匹配了,则层数减少,便把这一层的答案置0
num[top] = 0;
top--;
dp[i] = num[top] + 1;
num[top] = dp[i];//更新这一层答案
}
}
}
long long ans = 0;
for(int i = 1;i <= n;i++) ans += dp[i];//cout << dp[i] << " ";
cout << ans << endl;
return 0;
}
C:
数位dp。
题解说二分+dp。
其实二分的过程可以包含在dp的过程中了。不错的练手题,推荐数位dp欠缺(比如我)的人写,虽然这个dp代码量不大。
我的代码(严格也不算数位dp了):
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
long long power[20];
long long pow8[20];
long long k;
int d[20];
int main(){
power[0] = 1;
pow8[0] = 1;
for(int i = 1;i < 20;i++) power[i] = power[i - 1] * 10LL;
//cout << pow10[18] << endl;
for(int i = 1;i < 20;i++) pow8[i] = pow8[i - 1] * 8LL;
//cout << 9LL * (pow10[18] - pow8[18]) << endl;
cin >> k;
bool flag = false;
bool first = false;
for(int p = 19;p >= 1;p--){
long long sum = 0;
//cout << k << endl;
for(int j = 0;j <= 9;j++){
if(flag){
if(sum + power[p - 1] - ((!first && j == 0) ? 1 : 0 )>= k){
d[p] = j;
if(j != 0) first = true;
k -= sum;
break;
}else{
sum = sum + power[p - 1] - (!first && j == 0);
}
}else{
if(j == 4 || j == 7){
if(sum + power[p - 1] >= k){
d[p] = j;
if(j != 0) first = true;
k -= sum;
flag = true;
break;
}else{
sum = sum + power[p - 1];
//if(p == 2) cout << sum << endl;
}
}else{
if(sum + power[p - 1] - pow8[p - 1] >= k){
d[p] = j;
if(j != 0) first = true;
k -= sum;
break;
}else{
sum = sum + power[p - 1] - pow8[p - 1];
//if(p == 2) cout << sum << endl;
}
}
}
}
}
first = false;
for(int i = 19;i >= 1;i--){
if(!d[i] && !first) continue;
else{
if(d[i] > 0) first = true;
printf("%d",d[i]);
}
}
return 0;
}