数据结构题目

懒得写代码的几个题就恬不知耻……
请原谅我……

1.给你N个数 求平均值最大的子区间

做法
求出最大的一个值为答案
不嫌麻烦可以二分答案根据最大子段和判断

T1

#include 
#include 
using namespace std; 
  
int n,ans; 
  
int main(){ 
    cin>>n; 
    for(int i=1;i<=n;++i){ 
        int sum; 
        cin>>sum; 
        ans=max(ans,sum); 
    } 
    cout<

Else

#include 
#include 
#define N 100005  
using namespace std; 
  
int n; 
int s[N]; 
int l=0,r=0; 
  
bool ok(int k){ 
    int maxl=-1,sigma=0; 
    int sl=1,sr; 
    for(int i=1;i<=n;++i){ 
        sigma+=s[i];sr=i; 
        maxl=max(maxl,sigma); 
        if(sigma<0)sigma=0,sl=i+1; 
    } 
    if((sigma-(sr-sl+1)*k)>=0){ 
        l=sl,r=sr; 
        return 1; 
    } 
    else return 0; 
} 
  
int main(){ 
    cin>>n; 
    for(int i=1;i<=n;++i)cin>>s[i]; 
    int l=1,r=0x7fffff,mid; 
    while(l>1; 
        if(ok(mid))l=mid+1; 
        else r=mid-1; 
    } 
    cout<

2.给定N个数 求 min(ai,ai+1,……,aj)*|i-j|的最大值

做法
求出区间最小值
然后递归最小值左右两边

#include 
#include 
using namespace std; 
  
int n; 
int ans; 
int minl; 
int sum[N]; 
int c[N]; 
  
void change(int r){ 
    c[r]=num[r]; 
    for(int i=1;i=lowbit(r);r-=lowbit(r)) 
            ret=min(ret, c[r]); 
    } 
    return ret; 
} 
  
void find(int l,int r){ 
    if(l==r)return ; 
    int maxt,pos; 
    for(int i=l;i<=r;++i) 
        if(sum[i]>maxt){ 
            pos=i; 
            maxt=sum[i]; 
        } 
    ans=max((r-l)*mint,ans); 
    find(l,pos-1); 
    find(pos+1,r); 
} 
  
int main(){ 
    cin>>n; 
    for(int i=1;i<=n;++i)cin>>sum[i]; 
    find(1,n); 
    cout<

3.给定N个数 求 min(ai,aj)*|i-j|的最大值

要求线性
做法
一直去掉序列左右两端中小的元素

#include 
#include 
#define N 100005 
using namespace std; 
  
int sum[N]; 
  
int main(){ 
    int n; 
    cin>>n; 
    for(int i=1;i<=n;++i) 
        cin>>sum[i]; 
    l=1,r=n; 
    int ans=0; 
    while(l

4.给定N个数 要求对于所有的子区间求出其中位数

复杂度要求N^2
做法
维护一个数据结构
支持插入一个数,求出中位数,弹出中位数

#include 
#include 
#include 
#define N 10005 
using namespace std; 
  
int s[N]; 
int median; 
  
struct median_queue{ 
    int size; 
    priority_queue,less>q1; 
    priority_queue,greater>q2; 
    median_queue(){ 
        num=0; 
    } 
    void push(int s){ 
        num++; 
        if(size==1) 
                q1.push(b); 
        else{ 
            if(b<=q1.top()){ 
                q1.push(b); 
                if(q1.size()-q2.size()>1){ 
                    q2.push(q1.top()); 
                    q1.pop(); 
                } 
            } 
            else{ 
                q2.push(b); 
                if(q1.size()>n; 
    for(int i=1;i<=n;++i)cin>>s[i]; 
    for(int i=1;i<=n;++i){ 
        clear(); 
        for(int j=i;j<=n;++j){ 
            que.push(s[j]); 
            median=que.top(); 
        } 
    } 
     return 0; 
} 

5.N行N列的点阵? k个特殊点 (xi,yi) ,安全性 w(a,b)=min(|a-xi|+|b-yi|)

求一条从 (1,1) 到 (n,n) 的路径 ,最大化路径上安全性的最小值
要求平方算法

做法
通过BFS预处理出一个点到最近的特殊点的距离
然后二分路径上离特殊点最近的距离
通过并查集等维护联通性

#include
#include
#include
using namespace std;
const int maxn = 1003; 
int que[maxn*maxn][2],cnt=0;
int map[maxn][maxn];
int dis[maxn][maxn];
struct Node{
    int x,y;
}node[400];
int fs[5]={1,0,-1,0,1};
int n,m;
void bfs() {
    int head=0,tail=cnt;
    while(head<=tail) {
        int x=que[++head][0],y=que[head][1];
        for(int i=0;i<4;i++) {
            int xx=x+fs[i],xy=y+fs[i+1];
            if(xx>=1&&xx<=n&&xy>=1&&xy<=m&&!map[xx][xy]&&!dis[xx][xy]) {
                dis[xx][xy]=dis[x][y]+1;
                que[++tail][0]=xx,que[tail][1]=xy;
            }
        }
    }
}
bool vis[maxn][maxn];
bool judge(int ans) {
    int head=0,tail=1;
    memset(que,0,sizeof que);
    memset(vis,0,sizeof vis);
    que[1][0]=1,que[1][1]=1,vis[1][1]=1;
    while(head=1&&xx<=n&&xy>=1&&xy<=m&&dis[xx][xy]>=ans&&!map[xx][xy]&&!vis[xx][xy]) {
                if(xx==n&&xy==m)return true;
                que[++tail][0]=xx,que[tail][1]=xy;vis[xx][xy]=1;
            }
        }
    }
    return false;
}
int main() {
    //freopen("run.in","r",stdin);
    //freopen("run.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&map[i][j]);
            if(map[i][j]==1)que[++cnt][0]=i,que[cnt][1]=j;
            }
    if(map[1][1]||map[n][m]){
        puts("0");return 0;
    }
    bfs();
    int l=0,r=1000006;
    int ans;
    while(l<=r) {
        int mid=(l+r)/2;
        if(judge(mid)) {
            l=mid+1;
            ans=mid;
        }
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

简单的分数规划问题

给定两个序列A B
求一个最大的子区间满足
这段区间的A的区间和除以B的区间和最大

分数规划问题
一般做法是二分答案
二分一个m
是否有一个区间其答案变成m
然后变换成a[l,r]-mb[l,r]>=0
令c[i]=a[i]-m
b[i]
所以变成c[l,r]=0等价于是否存在c[i]>=0
因为二分的是实数
所以二分50次

#include 
#include 
#define N 100005 
using namespace std; 
  
int n; 
int A[N],B[N]; 
  
bool can(double s){ 
    for(int i=1;i<=n;++i) 
        if(A[i]-b[i]*s>=0)return true; 
    return false; 
} 
  
int main(){ 
    cin>>n; 
    for(int i=1;i<=n;++i)cin>>A[i]>>B[i]; 
    double l=1,r=10000000,mid; 
    for(int i=1;i<=50;++i){ 
        mid=(l+r)>>1; 
        if(can(mid))l=mid+1,ans=mid; 
        else r=mid+1; 
    } 
    cout<

极差

给出N个正整数,求出最长的一段,
使得该段内的最大值减去最小值的结果不超过给出的K。

2096: [Poi2010]Pilots

Time Limit: 30 Sec Memory Limit: 162 MB
Submit: 962 Solved: 501
[Submit][Status][Discuss]

Description

Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,
他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,
任意两个难度差不会超过他设定的最大值。耍畸形一个人是不行的,
于是他找到了你。

Input

输入:第一行两个有空格隔开的整数k(\(0\leq k\leq 2*10^{9}\)),n(\(1\leq n\leq 3*10^6\)),
k代表Tz设定的最大值,n代表难度序列的长度。
第二行为n个由空格隔开的整数(1<=ai<=2*10^9),表示难度序列。

Output

输出:最大的字串长度。

Sample Input

3 9
5 1 3 5 8 6 6 9 10

Sample Output

4

(有两个子串的长度为4: 5, 8, 6, 6 和8, 6, 6, 9.最长子串的长度就是4)
1、N<=10^52、N<=3*10^6

做法:单调队列维护单增单减队列

 从1到n依次加值
若极差大于k则将区间左端点右移到最小元素的右边
#include
#include
#define N 3000005
#define inf 1000000000
#define ll long long
using namespace std;

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int K,n,ans=1;
int a[N],q1[N],q2[N];

void solve(){
    int l1=1,l2=1,r1=0,r2=0,t=1;
    for(int i=1;i<=n;i++)
    {
        while(l1<=r1&&a[i]>=a[q1[r1]])r1--;
        while(l2<=r2&&a[i]<=a[q2[r2]])r2--;
        q1[++r1]=i;q2[++r2]=i;
        while(a[q1[l1]]-a[q2[l2]]>K)
            if(q1[l1]

疯狂的染色

每次将[L,R]染成某种颜色。问最后每个的颜色。
并查集维护
首先因为将一段区间染色之后还可能会重新染色
所以之前染的颜色会被后来染的颜色覆盖
所以从后往前染色就不用更改颜色的
并查集维护的是一个点染色的区间后第一个点的位置
然后这样之后只有没染过色的点才能被染色

#include 
#include
#define N 1000005
using namespace std;

int l,r;
int n,m,p,q;
int fa[N],res[N];

void read(int &s){
    char ch=getcahr();
    for(;!isdigit(ch);ch=getchar());
    for(s=0;isdigit();s=s*10-'0',ch=getchar());
}

inline int find(int x) {
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

int main(){
    read(n);read(m);read(p);read(q);
    for(int i=n+1;i;i--)fa[i]=i;
    for(int i=m;i;i--){
        l=(i*p+q)%n+1,r=(i*q+p)%n+1;
        if(l>r)swap(l,r);
        for(int x=find(l);x<=r;x=find(x))
            res[x]=i,fa[x]=find(x+1);
    }
    for(int i=1;i<=n;i++)printf("%d\n",res[i]);
    return 0;
}

N个数M次操作

修改一个数或者询问某一段的最大子段和
N,M<=10^5
GSS 1+2+3

用线段树维护
维护一段区间的最大前缀,最大子段和,最大后缀
利用了分治的思想
因为一段区间被分成两端区间
其最大子段和只有三种情况
数据结构题目_第1张图片
所以只要维护这三个值就可以
如果单点修改的话就重建区间就好

#include
#include
#define lc k<<1
#define rc k<<1|1

using namespace std;
const int M=1e5+5,N=M<<2;
struct sgtment{
    int sum,gss,lgss,rgss;
}tr[N];
int n,m,a[N];
void updata(int k){
    tr[k].sum=tr[lc].sum+tr[rc].sum;
    tr[k].lgss=max(tr[lc].lgss,tr[lc].sum+tr[rc].lgss);
    tr[k].rgss=max(tr[rc].rgss,tr[rc].sum+tr[lc].rgss);
    tr[k].gss=max(max(tr[lc].gss,tr[rc].gss),tr[lc].rgss+tr[rc].lgss);
}
void build(int k,int l,int r){
    if(l==r){
        tr[k].sum=tr[k].gss=tr[k].lgss=tr[k].rgss=a[l];
        return ;
    }
    int mid=l+r>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    updata(k);
}
void change(int k,int l,int r,int pos,int val){
    if(l==r){
        tr[k].sum=tr[k].gss=tr[k].lgss=tr[k].rgss=val;
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) change(lc,l,mid,pos,val);
    else change(rc,mid+1,r,pos,val);
    updata(k);
}
sgtment query(int k,int l,int r,int x,int y){
    if(l==x&&r==y) return tr[k];
    int mid=l+r>>1;
    if(y<=mid) return query(lc,l,mid,x,y);
    else if(x>mid) return query(rc,mid+1,r,x,y);
    else{
        sgtment left,right,result;
        left=query(lc,l,mid,x,mid);
        right=query(rc,mid+1,r,mid+1,y);
        result.sum=left.sum+right.sum;
        result.lgss=max(left.lgss,left.sum+right.lgss);
        result.rgss=max(right.rgss,right.sum+left.rgss);
        result.gss=max(max(left.gss,right.gss),left.rgss+right.lgss);
        return result;
    } 
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1,opt,x,y;i<=m;i++){
        scanf("%d%d%d",&opt,&x,&y);
        if(opt) printf("%d\n",query(1,1,n,x,y).gss);
        else change(1,1,n,x,y);
    }
    return 0;
}

N个数M次操作

将某个区间的数开方或者询问某一段的子段和
N,M\(\leq 10^{5}\)
暴力维护就可以
用一个标记表示区间被整体开方了几次
大于一定数量其值一定就为0

GSS 1 2 3的改版

每次询问的时候
给定左端点落在的区间和右端点落在的区间
询问最大子段和
N,M<=10^5
做法:维护区间最大前缀和,最大后缀和
因为给定区间左端点所在区间和右端点所在区间
所以求左端点部分用后缀和
求右端点部分用前缀和(GSS 5)

BZOJ 3333

题目大意:给定一个序列,每次选择一个位置,
把这个位置之后所有小于等于这个数的数抽出来,
排序,再插回去,求每次操作后的逆序对数
首先我们每一次操作 对于这个位置前面的数
由于排序的数与前面的数位置关系不变 所以这些数
的逆序对不会变化对于这个位置后面比这个数大的数
由于改变位置的数都比这些数小
所以这些数的逆序对不会变化
说到底就是排序的数的逆序对数改变了
以这些数开始的逆序对没有了
于是就好办了 我们用树状数组统计出以每个数
开始的逆序对数 然后以原数的大小为关键字建立线段树
维护区间最小值
对于每个询问p,我们取出[p,n]中的最小值a[x],
将a[x]清为正无穷,把以a[x]开头的逆序对减掉,
继续找,直到a[p]为正无穷为止
每个数只会被找到1次 所以均摊复杂度O(nlogn)

#include  
#include  
#include  
#include  
#define M 500500  
#define ls tree[p].lson  
#define rs tree[p].rson  
using namespace std;  
struct abcd{  
    int lson,rson;  
    int *num;  
}tree[M<<1];int tree_tot;  
int n,m,tot,a[M];  
pairb[M];  
int c[M],f[M];  
long long ans;  

int* _min(int *x,int *y)  {  
    return *x>=*y?y:x;  
}  
void Build_Tree(int p,int x,int y)  {  
    int mid=x+y>>1;  
    if(x==y)  
    {  
        tree[p].num=a+mid;  
        return ;  
    }  
    ls=++tree_tot;rs=++tree_tot;  
    Build_Tree(ls,x,mid);  
    Build_Tree(rs,mid+1,y);  
    tree[p].num=_min(tree[ls].num,tree[rs].num);  
}  
int* Get_Ans(int p,int x,int y,int l,int r)  {  
    int mid=x+y>>1;  
    if(x==l&&y==r)  
        return tree[p].num;  
    if(r<=mid)  
        return Get_Ans(ls,x,mid,l,r);  
    if(l>mid)  
        return Get_Ans(rs,mid+1,y,l,r);  
    return _min( Get_Ans(ls,x,mid,l,mid) , Get_Ans(rs,mid+1,y,mid+1,r) );  
}  
inline void Modify(int p,int x,int y,int pos)  {  
    int mid=x+y>>1;  
    if(x==y)  
        return ;  
    if(pos<=mid)  
        Modify(ls,x,mid,pos);  
    else  
        Modify(rs,mid+1,y,pos);  
    tree[p].num=_min(tree[ls].num,tree[rs].num);  
}  
inline void Update(int x)  {  
    for(;x<=tot;x+=x&-x)  
        c[x]++;  
}  
inline int Get_Ans(int x)  {  
    int re=0;  
    for(;x;x-=x&-x)  
        re+=c[x];  
    return re;  
}  
int main()  {  
    int i,p;  
    cin>>n>>m;  
    for(i=1;i<=n;i++)  
        scanf("%d",&b[i].first),b[i].second=i;  
    sort(b+1,b+n+1);  
    for(i=1;i<=n;i++)  {  
        if(i==1||b[i].first!=b[i-1].first)  
            ++tot;  
        a[b[i].second]=tot;  
    }  
    for(i=n;i;i--)  
        Update(a[i]),ans+=f[i]=Get_Ans(a[i]-1);  
    Build_Tree(0,1,n);  
    printf("%lld\n",ans);  
    for(i=1;i<=m;i++)  {  
        int *temp;  
        scanf("%d",&p);  
        if(a[p]!=0x3f3f3f3f)  
            do{  
                temp=Get_Ans(0,1,n,p,n);  
                ans-=f[temp-a];  
                *temp=0x3f3f3f3f;  
                Modify(0,1,n,temp-a);  
            }while(temp!=a+p);  
        printf("%lld\n",ans);  
    }  
}  




图上有黑白两种点

两种操作:
将某点异色
询问图中某类边的数量
N、M均为10^5

做法:

1.维护图中01,10,00边的数量
    单点修改则O(n)修改
2.按照点的大小将点分两种进行维护

\(lim = sqrt(m)\)
所以对于度数小于lim的点的修改,我们都
直接按上法去做算了,复杂度大约是\(n/2*sqrt(m)\)
那度数大于等于lim的点,我们希望不要每次去查和他相邻的每个点,
那么就分别保存这个点的和它相邻是0的点之间的边权和sum[i][0] 以及
这个点的和它相邻是1的点之间的边权和sum[i][1]。
如果这个点的这个sum值是自己记录的,只会影响到周围度数大于等于lim的点,
所以只要去修改这个点周围度数大于等于lim的点j的sum[j][0]和sum[j][1],
复杂度大约是\(n/2*sqrt(m)\)

所以总复杂度\(n*sqrt(m)\)

#include
#include
#include
#include
#include0};
#include
#include
#include
#include
#include
#include
#define ll long long  
using namespace std;  

struct node{  
    int st;  
    ll w0,w1;  
} p[100005];  

struct edge{  
    ll u,v;  
    ll w;  
} e[100005],ed;  

int color[100005],deg[100005];  
ll ans[3];  

bool cmp(edge x,edge y){  
    return x.u>x.v;  
}  
int main(){  
    int n,m,i,Case;  
    ll u,v,w;  
    char s[10];  
    Case=1;  
    while(scanf("%d%d",&n,&m)!=EOF){  
        memset(deg,0,sizeof(deg));  
        for (i=1;i<=n;i++) scanf("%d",&color[i]);  
        map,ll> r;
        for (i=1;i<=m;i++){  
            scanf("%I64d %I64d %I64d",&u,&v,&w); 
            if(u>v) swap(u,v); 
            r[{u,v}]+=w;
        }  
        m=0;
        for(map,ll>::iterator it=r.begin();it!=r.end();++it){
            e[++m].u=(*it).first.first; e[m].v=(*it).first.second; e[m].w=(*it).second;
        }
        ans[0]=ans[1]=ans[2]=0;  
        for (i=1;i<=m;i++){  
            u=e[i].u; v=e[i].v;  
            deg[u]++; deg[v]++;  
            if (color[u]!=color[v]) ans[2]+=e[i].w;  
            else ans[color[u]]+=e[i].w;   
        }  
        for (i=1;i<=m;i++){  
            u=e[i].u; v=e[i].v;  
            if (deg[u]>deg[v])  
                swap(e[i].u,e[i].v);
        }  
        sort(e+1,e+m+1,cmp);  
        memset(p,0,sizeof(p));  
        for (i=1;i<=m;i++){  
            u=e[i].u; v=e[i].v;  
            if (p[u].st==0) p[u].st=i;  
            if (color[u]) p[v].w1+=e[i].w;  
            else p[v].w0+=e[i].w;    
        }  
        int q;  
        scanf("%d",&q);  
        printf("Case %d:\n",Case++);  
        while (q--){  
            int x,y;  
            scanf("%s",s);  
            if(s[0]=='A'){  
                scanf("%d%d",&x,&y);  
                if(x!=y) printf("%I64d\n",ans[2]);  
                else printf("%I64d\n",ans[x]);  
            }  
            else{  
                scanf("%d",&x);  
                if(color[x]) {   
                    ans[2]+=p[x].w1;
                    ans[2]-=p[x].w0;  
                    ans[0]+=p[x].w0;  
                    ans[1]-=p[x].w1;  
                }  
                else{  
                    ans[2]-=p[x].w1;
                    ans[2]+=p[x].w0;  
                    ans[0]-=p[x].w0;  
                    ans[1]+=p[x].w1;    
                }  
                color[x]=1-color[x];  
                int st=p[x].st;  
                while(st<=m&&e[st].u==x){  
                    v=e[st].v;  
                    if(color[x]!=color[v]){  
                        ans[2]+=e[st].w;  
                        ans[1-color[x]]-=e[st].w;  
                    }  
                    else{  
                        ans[color[x]]+=e[st].w;  
                        ans[2]-=e[st].w;  
                    }  
                    if(color[x]==0){  
                        p[v].w0+=e[st].w;  
                        p[v].w1-=e[st].w;  
                    }  
                    else{  
                        p[v].w0-=e[st].w;  
                        p[v].w1+=e[st].w;  
                    }  
                    st++;  
                }  
            }  
        }  
    }  
    return 0;  
}  




给出N根木棍

有长度有颜色
问能否找到三根颜色不同的木棍组成三角形
木棍总数不超过10^6
考虑恰好组不成三角形的情况
设三条边为\(a_1,a_2,a_3\)
若对于任意\(a_i(1\leq i\leq 3)\),都有\(a_j+a_k>a_i(i,j,k\in{1,2,3}) 则可构成三角形 则若有一\)a_j+a_k=a_i$则恰好构不成一个三角形
观察上述式子,发现他与斐波那契数列的递推公式非常像:
\[f_i=f_{i-1}+f_{i-2}(i\leq i)\]
则易看出
如果一个数列中所有的数任取\(i,j,k\)都不能相互满足\(i+j>k\)
则数列中所有数必定满足,若将数字从小到大排序
\(a_i\leq a_{i-1}+a_{i-2}\)
所以可以根据这个来判断是否能组成三角形
换种思路,若一个数列的数相互构不成三角形
则数列的大小一定受到限制
对于一般的数据范围不超过int \((2^31-1)\)
斐波那契数在第47项就超过了
所以可以得出结论,如果木棍的数量超过47根
一定存在情况使得存在\(i+j>k\)



SPOJ 16549 QTREE6 - Query on a tree VI

给一棵树
每个点有黑有白
有反色操作
问与每个点联通的块中有多少个点
N,M<=100,000
记录每个点下方黑点和白点联通块点数个数
询问归到最上方结点
链修改单点查询
树状数组
DEC13 QTREE6

转载于:https://www.cnblogs.com/qdscwyy/p/7711251.html

你可能感兴趣的:(数据结构题目)