目录
过程记录
三个小时三道题
简单看了题,发现都有解题方法
oh 有朋自远方来,T3的代码是疯癫的亲爱的线段树呢
于是果断T1,目测代码最短的题目
+20min,T1代码成型,数据通过,顺利得诡异
然后T2,起初 莽 冲,思路没整理好,捏着各种算法,所以时间略长
+40min,T2结束,数据通过
最后,去招待久别的朋友线段树
+60min,长时间挠头后,把二叉树构建、区间查找、加法计算、以及两个不同操作同性质的work函数统统写了出来,然后组装
后过数据,喜洋洋,进洛谷,得50,哭唧唧
找bug,加法运算不能连等,小AC
144min,结束
惊悚发现T2少写了一个可能,但水过了
T1解
草率概括,给定数轴、起点和终点,按特定方式,求从起点到终点的最小步数
每一步三种方式,从x到x-1或x+1或2*x
可以联想到树,没有上下左右的dfs,所以弃dfs函数直接写递归
注意三种方法三倍数组,还有第一步在起点,起点已被走过
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+1;
int st,ed,step,loc=1,tot,a[N],b[N],fa[N];
int dfs(int x) {
if(fa[x]!=0) dfs(fa[x]);
tot++; }
int main() {
scanf("%d%d",&st,&ed);
if(st==ed) {
printf("0");
return 0; }
a[1]=st,b[st]=1;
while(step!=loc) {
step++;
for(register int i=1,x; i<=3; i++) {
if(i==1) x=a[step]-1;
if(i==2) x=a[step]+1;
if(i==3) x=a[step]*2;
if(x>=0&&x<=100000&&b[x]==0) {
a[++loc]=x,fa[loc]=step;
if(x==ed) {
dfs(loc),printf("%d",tot-1);
return 0; }
b[x]=1; } } }
return 0; }
T2解
草率概括++,给定一段数字串,求得到单调不递增或单调不递减的数字串的最小改变量
首先,单调不递增和单调不递减性质一样
其次,满足最小改变量的数字串可能不止一个
且其中一定有一个满足:在数字串中任一个数,要么无需改变,要么改变成前一个
则这个数字串里的数字与原数字串相同
然后,推一个式子,f(i,j)表示数字串前i个数字,且第i个数字为b[j]的改变量
f(i,j)=min.f(i-1,k)+abs(a[i]-b[j]),1<=k<=j
答案是min.f(n,_)
最后,注意单调不递增和不递减,以及2^32
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e3+1;
int n,a[N],b[N];
ll k,ans,dp[N][N];
bool cmp(int a,int b) {
return a>b; }
int main() {
scanf("%d",&n);
for(register int i=1; i<=n; i++)
scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
for(register int i=1; i<=n; i++)
dp[1][i]=abs(a[1]-b[i]);
for(register int i=2; i<=n; i++) {
k=dp[i-1][1];
for(register int j=1; j<=n; j++) {
k=min(dp[i-1][j],k);
dp[i][j]=k+abs(a[i]-b[j]); } }
ans=dp[n][1];
for(register int i=2; i<=n; i++)
ans=min(ans,dp[n][i]);
sort(b+1,b+n+1,cmp);
for(register int i=1; i<=n; i++)
dp[1][i]=abs(a[1]-b[i]);
for(register int i=2; i<=n; i++) {
k=dp[i-1][1];
for(register int j=1; j<=n; j++) {
k=min(dp[i-1][j],k);
dp[i][j]=k+abs(a[i]-b[j]); } }
for(register int i=1; i<=n; i++)
ans=min(ans,dp[n][i]);
printf("%lld",ans);
return 0; }
T3解
又是草率概括,给定一段数字串,多次操作
操作共两种,区间内加法,区间内比较大小
线段树,建树,两种操作
附有两种简化,两种操作局限,可以直接判断区间最大值最小值,也可以合并子树
建树时,每一节点是结构体最大最小值和懒标记
输入用数组,数据开四倍
#include<bits/stdc++.h>
#define ls(x) x<<1
#define rs(x) x<<1|1
using namespace std;
const int N=1e6+1;
int n,T,a[N];
inline int read() {
register int x=0;
register char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x; }
struct node {
int minn,maxx,st; } t[N*4];
inline void work1(int x) {
t[x].maxx=max(t[ls(x)].maxx,t[rs(x)].maxx);
t[x].minn=min(t[ls(x)].minn,t[rs(x)].minn); }
inline void work2(int x) {
if(t[x].st) {
int y=t[x].st;
t[x].st=0;
t[ls(x)].st+=y,t[rs(x)].st+=y;
t[ls(x)].minn+=y,t[rs(x)].minn+=y;
t[ls(x)].maxx+=y,t[rs(x)].maxx+=y; } }
void build(int x,int l,int r) {
if(l==r) {
t[x].minn=t[x].maxx=a[l];
return; }
int y=(l+r)>>1;
build(ls(x),l,y),build(rs(x),y+1,r);
work1(x); }
void add(int x,int l,int r,int sx,int sy,int k) {
if(l>=sx&&r<=sy) {
t[x].st+=k,t[x].minn+=k,t[x].maxx+=k;
return; }
work2(x);
int y=(l+r)>>1;
if(sx<=y) add(ls(x),l,y,sx,sy,k);
if(sy>y) add(rs(x),y+1,r,sx,sy,k);
work1(x); }
int query(int x,int l,int r,int sx,int sy,int k) {
if(l>=sx&&r<=sy) {
if(t[x].minn>=k) return r-l+1;
if(t[x].maxx<k) return 0; }
work2(x);
int ans=0,m=(l+r)>>1;
if(sx<=m) ans+=query(ls(x),l,m,sx,sy,k);
if(sy>m) ans+=query(rs(x),m+1,r,sx,sy,k);
return ans; }
int main() {
n=read(),T=read();
for(register int i=1; i<=n; i++) a[i]=read();
build(1,1,n);
for(register int i=1,x,y,z; i<=T; i++) {
char ch[10];
cin>>ch,x=read(),y=read(),z=read();
if(ch[0]=='M') add(1,1,n,x,y,z);
if(ch[0]=='A') printf("%d\n",query(1,1,n,x,y,z)); }
return 0; }