The Preliminary Contest for ICPC China Nanchang 南昌网络赛 A H I J 线段树,主席树,lca(注意这才是正常的lca,不要用以前的模板拉!)

96 篇文章 1 订阅
25 篇文章 1 订阅

其它一些是队友做的,我就写自己做的这几个吧

A. PERFECT NUMBER PROBLEM

Write a program to output the first 55 perfect numbers. A perfect number is defined to be a positive integer where the sum of its positive integer divisors excluding the number itself equals the number.

For example: 1+ 2 + 3 = 61+2+3=6, and 66 is the first perfect number.

There is no input for this problem.

题意:

让你输出前五个完数,一个数如果等于它所有因子的和(不包括它本身)那么被称为完数。

题解:

得出前面4个,那么就可以用一些不可描述的方法得出第五个

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    printf("6\n28\n496\n8128\n33550336\n");
    return 0;
}

H. Coloring Game

David has a white board with 2 \times N2×N grids.He decides to paint some grids black with his brush.He always starts at the top left corner and ends at the bottom right corner, where grids should be black ultimately.

Each time he can move his brush up(↑), down(↓), left(←), right(→), left up(↖), left down(↙), right up(↗), right down (↘) to the next grid.

For a grid visited before,the color is still black. Otherwise it changes from white to black.

David wants you to compute the number of different color schemes for a given board. Two color schemes are considered different if and only if the color of at least one corresponding position is different.

Input
One line including an integer n(0<n \le 10^9)n(0<n≤10
9
)

Output
One line including an integer, which represent the answer \bmod 1000000007mod1000000007

样例输入1 复制
2
样例输出1 复制
4
样例解释1
在这里插入图片描述

样例输入2 复制
3
样例输出2 复制
12
样例解释2
在这里插入图片描述

题意:

给你一个2*n的矩形,他会从最左上角到最右下角,每到一个格子会涂黑,可以走的方向是周围的8个方向,问你走完后有多少个不同样子的矩阵

题解:

由于它只是2*n的,所以这道题就变得异常简单,除了最左边和最右边的两列,其它要么是上面一格涂黑或者下面一格涂黑或者两格都涂黑,那么根据乘法原理,是 3 n − 2 3^{n-2} 3n2,那么最左边和最右边只有两个可以改变的格子,总共有2*2种可能,那么答案就是2*2* 3 n − 2 3^{n-2} 3n2

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
ll qpow(ll a,ll b)
{
    ll ret=a,ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*ret%mod;
        ret=ret*ret%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    ll n;
    scanf("%lld",&n);
    if(n==1)
        return 0*printf("1\n");
    printf("%lld\n",(4ll*qpow(3,n-2))%mod);
    return 0;
}

I. Max answer

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input
First line contains an integer n(1 \le n \le 5 \times 10 ^5n(1≤n≤5×10
5
).

Second line contains nn integers represent the array a (-10^5 \le a_i \le 10^5)a(−10
5
≤a
i

≤10
5
).

Output
One line contains an integer represent the answer of the array.

样例输入 复制
5
1 2 3 4 5
样例输出 复制
36

题意:

给你一个数组,让你找出其中的一段区间使得这段区间的和*这个区间的最小值 最大。

题解:

以前cf做过很像的题目啊,然后就直接A了。
用单调栈处理出每个值作为最小值的情况能够影响的最大区间范围,然后for一遍,如果这个值是正数,那么就是它能影响的区间的值的和,因为他是正数,所以区间越大区间和也就越大。还有一种负数的情况,负数的话那么要让这个区间和最小,那就可以用前缀和。这个数是负数的话,那我们只需要找到从它到它能影响到的范围的最右边前缀和的最小值-从它到它能影响到的最左边这个区间前缀和的最大值即可。但是如果左边的最大值<0的话,就不需要减掉,因为负数-负数是会变大的。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+5;
ll pre[N],a[N];
ll maxn[N*4],minn[N*4];
void build(int l,int r,int root)
{
    if(l==r)
    {
        maxn[root]=minn[root]=pre[l];
        return ;
    }
    int mid=l+r>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);
    maxn[root]=max(maxn[root<<1],maxn[root<<1|1]);
    minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
ll qmax(int l,int r,int root,int ql,int qr)
{
    if(l>=ql&&r<=qr)
        return maxn[root];
    int mid=l+r>>1;
    ll ans=-1e18;
    if(mid>=ql)
        ans=qmax(l,mid,root<<1,ql,qr);
    if(mid<qr)
        ans=max(ans,qmax(mid+1,r,root<<1|1,ql,qr));
    return ans;
}
ll qmin(int l,int r,int root,int ql,int qr)
{
    if(l>=ql&&r<=qr)
        return minn[root];
    int mid=l+r>>1;
    ll ans=1e18;
    if(mid>=ql)
        ans=qmin(l,mid,root<<1,ql,qr);
    if(mid<qr)
        ans=min(ans,qmin(mid+1,r,root<<1|1,ql,qr));
    return ans;
}
int lef[N],rig[N],st[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]),pre[i]=pre[i-1]+a[i];
    build(1,n,1);
    int top=0;
    //rig
    for(int i=1;i<=n;i++)
    {
        while(top&&a[st[top]]>a[i])
            rig[st[top--]]=i-1;
        st[++top]=i;
    }
    while(top)
        rig[st[top--]]=n;
    //lef
    for(int i=n;i>=1;i--)
    {
        while(top&&a[st[top]]>a[i])
            lef[st[top--]]=i+1;
        st[++top]=i;
    }
    while(top)
        lef[st[top--]]=1;
    ll ans=-1e18;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>0)
            ans=max(ans,a[i]*(pre[rig[i]]-pre[lef[i]-1]));
        else
            ans=max(ans,a[i]*(qmin(1,n,1,i,rig[i])-max(0ll,qmax(1,n,1,lef[i],i))));
    }
    printf("%lld\n",ans);
    return 0;
}

J. Distance on the tree

DSM(Data Structure Master) once learned about tree when he was preparing for NOIP(National Olympiad in Informatics in Provinces) in Senior High School. So when in Data Structure Class in College, he is always absent-minded about what the teacher says.

The experienced and knowledgeable teacher had known about him even before the first class. However, she didn’t wish an informatics genius would destroy himself with idleness. After she knew that he was so interested in ACM(ACM International Collegiate Programming Contest), she finally made a plan to teach him to work hard in class, for knowledge is infinite.

This day, the teacher teaches about trees." A tree with nn nodes, can be defined as a graph with only one connected component and no cycle. So it has exactly n-1n−1 edges…" DSM is nearly asleep until he is questioned by teacher. " I have known you are called Data Structure Master in Graph Theory, so here is a problem. “” A tree with nn nodes, which is numbered from 11 to nn. Edge between each two adjacent vertexes uu and vv has a value w, you’re asked to answer the number of edge whose value is no more than kk during the path between uu and vv."" If you can’t solve the problem during the break, we will call you DaShaMao(Foolish Idiot) later on."

The problem seems quite easy for DSM. However, it can hardly be solved in a break. It’s such a disgrace if DSM can’t solve the problem. So during the break, he telephones you just for help. Can you save him for his dignity?

Input
In the first line there are two integers n,mn,m, represent the number of vertexes on the tree and queries(2 \le n \le 10^5,1 \le m \le 10^52≤n≤10
5
,1≤m≤10
5
)

The next n-1n−1 lines, each line contains three integers u,v,wu,v,w, indicates there is an undirected edge between nodes uu and vv with value ww. (1 \le u,v \le n,1 \le w \le 10^91≤u,v≤n,1≤w≤10
9
)

The next mm lines, each line contains three integers u,v,ku,v,k , be consistent with the problem given by the teacher above. (1 \le u,v \le n,0 \le k \le 10^9)(1≤u,v≤n,0≤k≤10
9
)

Output
For each query, just print a single line contains the number of edges which meet the condition.

样例输入1 复制
3 3
1 3 2
2 3 7
1 3 0
1 2 4
1 2 7
样例输出1 复制
0
1
2
样例输入2 复制
5 2
1 2 1000000000
1 3 1000000000
2 4 1000000000
3 5 1000000000
2 3 1000000000
4 5 1000000000
样例输出2 复制
2
4

题意:

给你一棵树,每条边都有一个权值,m个询问,问你从x到y的路径上有多少条边的值<=w。

题解:

一开始用主席树+树链剖分,但是有点复杂,不知道哪里写错了就没过,后来发现,直接用lca就好了啊,非常敢单的一道题目。
但是由于我以前lca的模板过于垃圾,用了别人的板子才过,以后要注意用这个。
这样的话,我们就可以一边dfs一边建树,每一个点的状态是从它父亲的状态转移过来的。那么两个点之间的所有可能就是一个点到他们的lca+另一个点到他们的lca。注意存边权的话,是要存在儿子节点上的。那么主席树就是查找区间比k小的数的个数,以前有写过一道一模一样的题目,主席树就没有改动。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int ls[maxn*40],rs[maxn*40],sum[maxn*40],rt[maxn*40];
int tot,a[maxn],b[maxn];
int n,m;
struct node
{
    int to,next,wei;
}e[maxn<<1];
int head[maxn],fa[maxn][25],cnt,dep[maxn];
void add(int x,int y,int z)
{
    e[cnt].to=y;
    e[cnt].next=head[x];
    e[cnt].wei=z;
    head[x]=cnt++;
}
void update(int l,int r,int &root,int last,int val)
{
    root=++tot;
    ls[root]=ls[last];
    rs[root]=rs[last];
    sum[root]=sum[last]+1;
    if(l==r)
        return ;
    int mid=l+r>>1;
    if(mid>=val)
        update(l,mid,ls[root],ls[last],val);
    else
        update(mid+1,r,rs[root],rs[last],val);
}
int query(int l,int r,int ql,int qr,int num)
{
    if(l==r)
        return sum[qr]-sum[ql];
    int mid=l+r>>1;
    if(num<=mid)
        return query(l,mid,ls[ql],ls[qr],num);
    else
    {
        int ans=sum[ls[qr]]-sum[ls[ql]];
        ans+=query(mid+1,r,rs[ql],rs[qr],num);
        return ans;
    }
}
int all;
void dfs(int son,int ffa,int val)
{
    dep[son]=dep[ffa]+1;
    fa[son][0]=ffa;
    if(son!=1)
        update(1,maxn,rt[son],rt[ffa],val);
    for(int i=head[son];~i;i=e[i].next)
    {
        int ne=e[i].to;
        if(ne==ffa)
            continue;
        dfs(ne,son,lower_bound(b+1,b+1+all,e[i].wei)-b);
    }
}
void deal(){
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];

}

int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>=dep[y])
            x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    int x,y,z;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z),b[i]=z;
        add(x,y,z),add(y,x,z);
    }
    sort(b+1,b+n);
    all=unique(b+1,b+n)-(b+1);
    tot=0;
    dfs(1,1,0);
    deal();
    while(m--)
    {
        scanf("%d%d%d",&x,&y,&z);
        z=upper_bound(b+1,b+1+all,z)-b-1;
        if(z==0)
            printf("0\n");
        else
        {
            int l=lca(x,y),ans=0;
            if(x!=l)
                ans=query(1,maxn,rt[l],rt[x],z);
            if(y!=l)
                ans+=query(1,maxn,rt[l],rt[y],z);
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值