机房测试:折半+二分+判环+树上开桶

T1:

 

 分析:

如果x小的话,就直接背包。这道题中n很小,60%可以直接3^10暴搜,25的呢?明显是折半嘛。

先搜前一半的物品,用map记录能拼凑出的种类数,再搜后一半物品,直接查询统计答案即可。

#include<bits/stdc++.h>
using namespace std;
#define N 50
#define ll long long
#define ri register int
ll a[N],b[N],maxn[N],n,x,ans=0,n1,n2;
map<ll,int> mp;
int get(ll x)
{
    int cnt=0;
    while(x){
        cnt+=(x&1); x>>=1;
    }
    return cnt;
}
void dfs1(int now,ll tot)
{
    if(now>n1) { mp[tot]++; return ; }
    if(tot>x) return ;
    dfs1(now+1,tot+a[now]);
    dfs1(now+1,tot+b[now]);
    dfs1(now+1,tot);
}
void dfs2(int now,ll tot)
{
    if(now>n2) { ans+=mp[x-tot]; return ; }
    if(tot>x) return ;
    dfs2(now+1,tot+a[now]);
    dfs2(now+1,tot+b[now]);
    dfs2(now+1,tot);
}
int main()
{
    freopen("building.in","r",stdin);
    freopen("building.out","w",stdout);
    scanf("%d%lld",&n,&x);
    n1=(n+1)>>1,n2=n;
    for(ri i=1;i<=n;++i) scanf("%lld",&a[i]),b[i]=(1<<get(a[i]));
    dfs1(1,0);
    dfs2(n1+1,0);
    printf("%lld
",ans);
}
View Code

T2:

 

 分析:

简化一下题意:找出有向图中所有环,每个环内的边取min,所有环中的min取max。

很努力地想如何O(n)找到有向图中的所有环,但还是不知道。。。

对于最大值最小很显然可以换一个思路:二分!

二分一个值mid,如果存在一个环使得环上的所有边权都大于mid,就说明这个值不合法。

所以只需要挑边权小于等于mid的边跑一边,判断有没有环即可。

然后我脑残地用n^2去判环。。。导致只拿了个暴力分。。。

O(n)地判环可以tarjan,也可以拓扑。(这道题中优选tarjan,拓扑还要重新建边)

#include<bits/stdc++.h>
using namespace std;
#define ri register int
#define M 300005
#define N 200005
int n,m,to[M],nex[M],head[N],w[M],vis[N],tot=0;
int top=0,Ti=0,dfn[N],low[N],stk[N],flag[N];
void add(int a,int b,int c) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=c; }
bool tarjan(int u,int k)
{
    dfn[u]=low[u]=++Ti;
    stk[++top]=u; flag[u]=1;
    for(ri i=head[u];i;i=nex[i]){
        int v=to[i];
        if(w[i]<=k) continue;
        if(!dfn[v]){
            if(tarjan(v,k)) return true;
            low[u]=min(low[u],low[v]);
        }
        else if(flag[v]) { low[u]=min(low[u],dfn[v]); return true; }
    }
    if(dfn[u]==low[u]){
        do{
            int tmp=stk[top];
            flag[tmp]=0; 
        }while(stk[top--]!=u);
    }
    return false;
}
int main()
{
    freopen("pestc.in","r",stdin);
    freopen("pestc.out","w",stdout);
    scanf("%d%d",&n,&m);
    int a,b,c,mx=0;
    for(ri i=1;i<=m;++i) scanf("%d%d%d",&a,&b,&c),add(a,b,c),mx=max(mx,c);
    int l=0,r=mx+1,ans=0;
    while(l<r){
        int mid=(l+r)>>1,fl=1;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(flag,0,sizeof(flag));
        Ti=0; top=0;
        for(ri i=1;i<=n;++i)
        if(!dfn[i]){
            if(tarjan(i,mid)) { fl=0; break; }
        } 
        if(fl) r=mid,ans=mid;
        else l=mid+1;
    }
    printf("%d
",ans);
}
/*
4 5
3 4 8
1 4 9
3 1 6
4 2 1
2 4 8

3 4
1 2 3
2 1 1
2 3 2
3 1 4

9 13
1 2 3
2 3 2
3 4 5
4 1 9
1 5 6
5 6 7
6 1 8
3 7 6
7 3 3
8 7 9
7 8 11
8 9 12
9 7 13
*/
View Code

T3:

 

 分析:

我不会dsu on tree,也不会线段树合并。。。

但这道题其实可以利用天天爱跑步的思想做。

求小于,等于,大于一个值显然可以维护一颗值域树状数组(只需要单点修改,区间查询),也就是开一个桶。

设u的两个子节点v1和v2。

当v1遍历完后,桶中保留了v1及其所有子节点的信息。

这时我们遍历v2,将v2子树中的信息也加入桶里,那么统计v2的贡献时明显会统计到v1子树中的,怎么办呢?

只需要在将v2子树中信息加入前记录一下原来的,然后加入后再两者相减即可。

#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define ri register int
int n,sum[N],a[N],b[N],mn[N],sq[N],mx[N];
vector<int> e[N];
int query(int x)//维护值域树状数组 
{
    int ans=0;
    while(x>0){
        ans+=sum[x];
        x-=(x&-x);
    }
    return ans;//记得return 值。。。 
}
void modify(int x)
{
    while(x<=n){
        sum[x]+=1;
        x+=(x&-x);
    }
}
int get(int l,int r) { return query(r)-query(l-1); }
void dfs(int u,int ff)
{
    int pre_min=get(1,a[u]-1);//先求出非其子树的贡献 
    int pre_seq=get(a[u],a[u]);
    int pre_max=get(a[u]+1,n);
    for(ri i=0;i<e[u].size();++i){
        int v=e[u][i];
        if(v==ff) continue;
        dfs(v,u);
    }
    mn[u]=get(1,a[u]-1)-pre_min;//再除去非其子树的影响 
    sq[u]=get(a[u],a[u])-pre_seq;
    mx[u]=get(a[u]+1,n)-pre_max;
    modify(a[u]);
}
int main()
{
    freopen("ginkgo.in","r",stdin);
    freopen("ginkgo.out","w",stdout);
    scanf("%d",&n);
    int x;
    for(ri i=2;i<=n;++i) scanf("%d",&x),e[i].push_back(x),e[x].push_back(i);
    for(ri i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+1+n);
    int num=unique(b+1,b+1+n)-b-1;//离散化 
    for(ri i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+num,a[i])-b;
    swap(n,num);//!!
    dfs(1,0);
    for(ri i=1;i<=num;++i) printf("%d %d %d
",mn[i],sq[i],mx[i]);
}
/*
5
1 1 3 3
0 0 1 2 0
*/
View Code
原文地址:https://www.cnblogs.com/mowanying/p/11808405.html