zoukankan      html  css  js  c++  java
  • 数据结构题目

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

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

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

    T1

    #include<iostream> 
    #include<cstdio> 
    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<<ans; 
    } 
    

    Else

    #include<iostream> 
    #include<cstdio> 
    #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<r){ 
            mid=(l+r)>>1; 
            if(ok(mid))l=mid+1; 
            else r=mid-1; 
        } 
        cout<<l<<" "<<r; 
    } 
    

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

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

    #include<iostream> 
    #include<cstdio> 
    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);i<<=1) 
            c[r]=min(c[r],c[r-i]); 
    } 
      
    int getk(int l,int r){ 
        int ret=num[r]; 
        while(l<=r){ 
            ret=min(ret,num[r]); 
            for(--r;r-l>=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<<ans; 
        return 0; 
    } 
    

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

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

    #include<iostream> 
    #include<cstdio> 
    #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<r){ 
            int minl=min(sum[l],sum[r]) 
            ans=maxl((r-l)*minl,ans); 
            if(sum[l]<sum[r])l++; 
            else r++; 
        } 
        cout<<ans; 
        return 0; 
    } 
    

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

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

    #include<iostream> 
    #include<cstdio> 
    #include<queue> 
    #define N 10005 
    using namespace std; 
      
    int s[N]; 
    int median; 
      
    struct median_queue{ 
        int size; 
        priority_queue<int,vector<int>,less<int>>q1; 
        priority_queue<int,vector<int>,greater<int>>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()<q2.size()){ 
                        q1.push(q2.top()); 
                        q2.pop(); 
                       } 
                } 
            } 
        } 
        int top(){ 
            return q1.top();     
        } 
        int pop(){ 
            size--; 
            q1.pop(); 
            if(q1.size()<q2.size()){ 
                q1.push(q2.top()); 
                q2.pop(); 
            } 
        } 
        void clear(){ 
            size=0; 
            while(q1.size())q1.pop(); 
            while(q2.size())q2.pop(); 
        } 
    }que; 
      
    int main(){ 
        int n; 
        cin>>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<cstdio>
    #include<cstring>
    #include<iostream>
    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<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&&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
    ",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<iostream> 
    #include<cstdio> 
    #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<<ans; 
        return 0; 
    } 
    

    极差

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

    2096: [Poi2010]Pilots

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

    Description

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

    Input

    输入:第一行两个有空格隔开的整数k((0leq kleq 2*10^{9})),n((1leq nleq 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<iostream>
    #include<cstdio>
    #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]<q2[l2])t=q1[l1]+1,l1++;
    			else t=q2[l2]+1,l2++;
    		ans=max(ans,i-t+1);
    	}
    }
    
    int main(){
        K=read();n=read();
        for(int i=1;i<=n;i++)
        a[i]=read();
        solve();
        printf("%d
    ",ans);
        return 0;
    }
    

    疯狂的染色

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

    #include<iostream> 
    #include<cstdio>
    #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
    ",res[i]);
    	return 0;
    }
    

    N个数M次操作

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

    用线段树维护
    维护一段区间的最大前缀,最大子段和,最大后缀
    利用了分治的思想
    因为一段区间被分成两端区间
    其最大子段和只有三种情况

    所以只要维护这三个值就可以
    如果单点修改的话就重建区间就好

    #include<cstdio>
    #include<iostream>
    #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
    ",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<cstdio>  
    #include<cstring>  
    #include<iostream>  
    #include<algorithm>  
    #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];  
    pair<int,int>b[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
    ",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
    ",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<iostream>
    #include<algorithm>
    #include<string>
    #include<map>
    #include<vector>0};
    #include<set>
    #include<cmath>
    #include<stack>
    #include<string>
    #include<stdlib>
    #include<cstdio>
    #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<pair<ll,ll>,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<pair<ll,ll>,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:
    ",Case++);  
            while (q--){  
                int x,y;  
                scanf("%s",s);  
                if(s[0]=='A'){  
                    scanf("%d%d",&x,&y);  
                    if(x!=y) printf("%I64d
    ",ans[2]);  
                    else printf("%I64d
    ",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(1leq ileq 3)),都有(a_j+a_k>a_i(i,j,kin{1,2,3}) 则可构成三角形 则若有一)a_j+a_k=a_i$则恰好构不成一个三角形
    观察上述式子,发现他与斐波那契数列的递推公式非常像:

    [f_i=f_{i-1}+f_{i-2}(ileq i) ]

    则易看出
    如果一个数列中所有的数任取(i,j,k)都不能相互满足(i+j>k)
    则数列中所有数必定满足,若将数字从小到大排序
    (a_ileq 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

  • 相关阅读:
    C# 将Excel中的数据到DataSet中
    Struts select标签在 FreeMarker 中的使用。
    .Net 中显式实现接口
    C#抽象类和抽象方法的应用
    iframe 自动控制高
    兔子问题总结(总结)
    MySQL group_concat 方法的使用
    服务发现与负载均衡机制的实现
    ChannelFuture
    服务发布、订阅及远程通信
  • 原文地址:https://www.cnblogs.com/qdscwyy/p/7711251.html
Copyright © 2011-2022 走看看