Codeforces Round #353 (Div. 2)题解
2016-05-17 17:20
281 查看
比赛传送门:http://codeforces.com/contest/675
A:水题
判断是否在等差数列里,一定要分类讨论公差正负,b和a的大小,不能直接模
B:水题
5个位置变量设为x1到x5,发现4个正方形里都有x3,所以x3是有n种取法,接下来就是x1取定,另外3个都取定了,算出另外3个由x1表达的式子,判断是否在范围里即可
C:前缀和
给你n个数字,围成一个圈,然后总和为0,有一种操作是可以把一个数字,给周围两个数字中的一个一些值,就比如有-3 和3,3能给-3 3这么多值,然后他们就变成了0 0,这是一次操作,然后问你最少多少次操作,能变成全0
考虑一段值,和为0,他们变成全0,只需要长度-1次操作,这是肯定的,所以就是找出那些和为0的段
这个该怎么找呢,可以把前缀和都求出来,这样一段长度为n的前缀和数组,里面有些是0有些不是0,不是0都是成段出现,这连续的段的长度就是这一段需要移动的次数,所以只需要考虑长度n的前缀和数组里面不是0的个数
但是这n个数字是一个圈,所以不一定是从1开始到n,可能2到n+1之类的,所以需要把长度n补为2n,然后在里面考虑长度n的前缀和,当你考虑2到n+1的时候,应该找里面不为sum[1]的个数,因为这个前缀和是要减去sum[1]的
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 200005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
map<LL,int> ma;
int a[MAX];
LL sum[MAX];
int main(){
//freopen("in.txt","r",stdin);
int n;
while(cin>>n){
ma.clear();
sum[0]=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=n+1;i<=2*n;i++) sum[i]=sum[i-1]+a[i-n];
for(int i=1;i<=n;i++){
if(!ma.count(sum[i])) ma[sum[i]]=0;
ma[sum[i]]++;
}
int maxn=ma[0];
for(int i=n+1;i<=2*n;i++){
ma[sum[i-n]]--;
ma[sum[i]]++;
maxn=max(maxn,ma[sum[i-n]]);
}
cout<<n-maxn<<endl;
}
return 0;
}
D:排序+set
构造二叉搜索树,问你按照顺序放进去每个节点,它的父节点是什么
暴力肯定会TLE辣,如果是一条链,就退化到O(n^2)了
要考虑二叉搜索树的性质,把一棵二叉搜索树投影到横轴上,数列是有序的,所以可以先排序,然后按照顺序往里面放,记录每个点的左右儿子是否都已经被占领即可,每次放进去的时候找比它小的第一个和比它大的第一个就行,然后考虑比它小的的右儿子,比它大的的左儿子,必定只有一个满足(因为小的放进去,已经占领了大的左儿子,或者大的放进去占领了小的右儿子,所以必定只有一个解)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 100005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
set<int> s;
map<int,int> l,r;
int main(){
//freopen("in.txt","r",stdin);
int n;
while(cin>>n){
l.clear(),r.clear();s.clear();
for(int i=0;i<n;i++){
int a;
scanf("%d",&a);
if(i){
set<int>::iterator L=s.lower_bound(a);
set<int>::iterator R=s.lower_bound(a);
if(L==s.begin()){
printf("%d",*R);
l[*R]=a;
}
else if(R==s.end()){
L--;
printf("%d",*L);
r[*L]=a;
}
else{
L--;
if(r[*L]){
printf("%d",*R);
l[*R]=a;
}
else{
printf("%d",*L);
r[*L]=a;
}
}
}
s.insert(a);
if(i){
if(i==n-1) printf("\n");
else printf(" ");
}
}
}
return 0;
}
E:dp+线段树
给你第i个车站,可以买到i+1到ai的车站的票,p[i,j]为从i到j的用的最少的车票,问你sigma(p[i,j])
考虑p[i]为第i个车站往后面的所有车站走的最小值,能直接到i+1到ai,所以i+1 - ai之间的车站肯定是1次就能到达,后面的那些车站如何最小化呢,应该要在i+1 - ai之间找一个过渡点,哪个最优呢,我原本考虑是p[i]最小的,但是发现并不行,因为p[i]最小,不代表去掉i能1次到达的那些站之后还是最小,所以应该考虑区间里面能到达最远地方的那个车站,因为它能走的最远,它用一次票走到的地方更多,所以它最优,线段树维护下即可
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 100005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
LL p[MAX];
pii maxv[MAX<<2];
int a[MAX];
void pushup(int rt){
maxv[rt]=max(maxv[rrt],maxv[lrt]);
}
void build(int l,int r,int rt){
if(l==r){
maxv[rt]=mk(0,l);
return;
}
middle;
build(lson);
build(rson);
}
void update(int l,int r,int rt,int pos,int d){
if(l==r){
maxv[rt]=mk(d,pos);
return ;
}
middle;
if(pos<=m) update(lson,pos,d);
else update(rson,pos,d);
pushup(rt);
}
pii query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R) return maxv[rt];
middle;
pii u=mk(0,0);
if(L<=m) u=max(u,query(lson,L,R));
if(R>m) u=max(u,query(rson,L,R));
return u;
}
int main(){
//freopen("in.txt","r",stdin);
int n;
while(cin>>n){
for(int i=1;i<n;i++) scanf("%d",&a[i]);
p[n-1]=1;
build(1,n,1);
update(1,n,1,n-1,a[n-1]);
LL ans=p[n-1];
for(int i=n-2;i>0;i--){
pii u=query(1,n,1,i+1,a[i]);
p[i]=a[i]-i+(p[u.second]-(a[i]-u.second))+(n-a[i]);
update(1,n,1,i,a[i]);
ans+=(LL)p[i];
}
cout<<ans<<endl;
}
return 0;
}
A:水题
判断是否在等差数列里,一定要分类讨论公差正负,b和a的大小,不能直接模
B:水题
5个位置变量设为x1到x5,发现4个正方形里都有x3,所以x3是有n种取法,接下来就是x1取定,另外3个都取定了,算出另外3个由x1表达的式子,判断是否在范围里即可
C:前缀和
给你n个数字,围成一个圈,然后总和为0,有一种操作是可以把一个数字,给周围两个数字中的一个一些值,就比如有-3 和3,3能给-3 3这么多值,然后他们就变成了0 0,这是一次操作,然后问你最少多少次操作,能变成全0
考虑一段值,和为0,他们变成全0,只需要长度-1次操作,这是肯定的,所以就是找出那些和为0的段
这个该怎么找呢,可以把前缀和都求出来,这样一段长度为n的前缀和数组,里面有些是0有些不是0,不是0都是成段出现,这连续的段的长度就是这一段需要移动的次数,所以只需要考虑长度n的前缀和数组里面不是0的个数
但是这n个数字是一个圈,所以不一定是从1开始到n,可能2到n+1之类的,所以需要把长度n补为2n,然后在里面考虑长度n的前缀和,当你考虑2到n+1的时候,应该找里面不为sum[1]的个数,因为这个前缀和是要减去sum[1]的
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 200005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
map<LL,int> ma;
int a[MAX];
LL sum[MAX];
int main(){
//freopen("in.txt","r",stdin);
int n;
while(cin>>n){
ma.clear();
sum[0]=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=n+1;i<=2*n;i++) sum[i]=sum[i-1]+a[i-n];
for(int i=1;i<=n;i++){
if(!ma.count(sum[i])) ma[sum[i]]=0;
ma[sum[i]]++;
}
int maxn=ma[0];
for(int i=n+1;i<=2*n;i++){
ma[sum[i-n]]--;
ma[sum[i]]++;
maxn=max(maxn,ma[sum[i-n]]);
}
cout<<n-maxn<<endl;
}
return 0;
}
D:排序+set
构造二叉搜索树,问你按照顺序放进去每个节点,它的父节点是什么
暴力肯定会TLE辣,如果是一条链,就退化到O(n^2)了
要考虑二叉搜索树的性质,把一棵二叉搜索树投影到横轴上,数列是有序的,所以可以先排序,然后按照顺序往里面放,记录每个点的左右儿子是否都已经被占领即可,每次放进去的时候找比它小的第一个和比它大的第一个就行,然后考虑比它小的的右儿子,比它大的的左儿子,必定只有一个满足(因为小的放进去,已经占领了大的左儿子,或者大的放进去占领了小的右儿子,所以必定只有一个解)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 100005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
set<int> s;
map<int,int> l,r;
int main(){
//freopen("in.txt","r",stdin);
int n;
while(cin>>n){
l.clear(),r.clear();s.clear();
for(int i=0;i<n;i++){
int a;
scanf("%d",&a);
if(i){
set<int>::iterator L=s.lower_bound(a);
set<int>::iterator R=s.lower_bound(a);
if(L==s.begin()){
printf("%d",*R);
l[*R]=a;
}
else if(R==s.end()){
L--;
printf("%d",*L);
r[*L]=a;
}
else{
L--;
if(r[*L]){
printf("%d",*R);
l[*R]=a;
}
else{
printf("%d",*L);
r[*L]=a;
}
}
}
s.insert(a);
if(i){
if(i==n-1) printf("\n");
else printf(" ");
}
}
}
return 0;
}
E:dp+线段树
给你第i个车站,可以买到i+1到ai的车站的票,p[i,j]为从i到j的用的最少的车票,问你sigma(p[i,j])
考虑p[i]为第i个车站往后面的所有车站走的最小值,能直接到i+1到ai,所以i+1 - ai之间的车站肯定是1次就能到达,后面的那些车站如何最小化呢,应该要在i+1 - ai之间找一个过渡点,哪个最优呢,我原本考虑是p[i]最小的,但是发现并不行,因为p[i]最小,不代表去掉i能1次到达的那些站之后还是最小,所以应该考虑区间里面能到达最远地方的那个车站,因为它能走的最远,它用一次票走到的地方更多,所以它最优,线段树维护下即可
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 100005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
LL p[MAX];
pii maxv[MAX<<2];
int a[MAX];
void pushup(int rt){
maxv[rt]=max(maxv[rrt],maxv[lrt]);
}
void build(int l,int r,int rt){
if(l==r){
maxv[rt]=mk(0,l);
return;
}
middle;
build(lson);
build(rson);
}
void update(int l,int r,int rt,int pos,int d){
if(l==r){
maxv[rt]=mk(d,pos);
return ;
}
middle;
if(pos<=m) update(lson,pos,d);
else update(rson,pos,d);
pushup(rt);
}
pii query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R) return maxv[rt];
middle;
pii u=mk(0,0);
if(L<=m) u=max(u,query(lson,L,R));
if(R>m) u=max(u,query(rson,L,R));
return u;
}
int main(){
//freopen("in.txt","r",stdin);
int n;
while(cin>>n){
for(int i=1;i<n;i++) scanf("%d",&a[i]);
p[n-1]=1;
build(1,n,1);
update(1,n,1,n-1,a[n-1]);
LL ans=p[n-1];
for(int i=n-2;i>0;i--){
pii u=query(1,n,1,i+1,a[i]);
p[i]=a[i]-i+(p[u.second]-(a[i]-u.second))+(n-a[i]);
update(1,n,1,i,a[i]);
ans+=(LL)p[i];
}
cout<<ans<<endl;
}
return 0;
}
相关文章推荐
- 【转】SIFT特征提取分析
- [转]如何成为主管
- mate 8、荣耀6,手机连接eclipse、AS调试应用没有log输出
- JavaScript字符串处理函数 - split()、join()、substring()和indexOf()
- AndroidTV桌面BriskTVLanucher
- 二叉查找树(AVL)插入算法Java实现
- Android中进程和线程
- 一个简单的递归算法的思考
- hibernate相关问题
- 可能最有效的母亲节邮件主题,你用对了吗?
- Hive实战
- 流氓网站5599.net修改ie主页分析
- 977dh用vbs通过wsh创建桌面快捷方式,快速启动,修改ie主页的恶意代码 ...
- YII学习总结4(cookie操作)
- 用户体验---搜狗输入法
- 监控redis和zookpeer服务脚本 并且python发送邮件
- 关于Char* ,CString ,WCHAR*之间的转换问题
- iOS锄大地发牌算法
- liunx下的命令 Ctrl+z 和 Ctrl+c 有区别
- Json Web Token身份认证