• 2016 ACM/ICPC Asia Regional Shenyang Online


    I:QSC and Master

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5900

    题意:

      给出n对数keyi,vali表示当前这对数的键值和权值,可以操作将连续的两个数合并,如果满足gcd(a[i],a[i+1])>1,得到的价值是两个数的权值和,每次合并两个数之后,这两个数就会消失,然后旁边的数会接上比如1 2 3 4 合并了 2 3 则 剩下1 4也可以合并

    思路:区间dp

    1:处理出任意区间内的所有数是否可以合并

    对于当前的[l,r]区间,如果区间[l+1,r-1]可以合并,且gcd(a[l]+a[r])!=1的话,则整个区间[l,r]可以合并,价值也就是前缀和

    同理处理出其他的情况  [l,r-2] [l+1,r]

    2:区间dp,对于当前的l,r区间,如果可以合并,则直接加上区间和

    反之,则枚举一个中间值k,找出区间内最大满足情况的值

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=310;
    typedef long long ll;
    ll n;
    ll num[maxn],val[maxn];
    ll dp[maxn][maxn];
    ll out[maxn][maxn];
    ll sum[maxn];
    ll gcd(ll x,ll y){
        return x==0?y:gcd(y%x,x);
    }
    //确定1-n的最大值
    void Dp(){
        memset(out,0,sizeof(out));
        for(ll l=2;l<=n;l++){
            for(ll i=1;i+l-1<=n;i++){
                ll j=i+l-1;
                if(dp[i][j]==1)
                    out[i][j]=sum[j]-sum[i-1];
                else{
                    for(ll k=i;k<j;k++){
                        out[i][j]=max(out[i][j],out[i][k]+out[k+1][j]);
                    }
                }
            }
        }
    }
    //判断区间是否能合并
    void query(){
        memset(dp,0,sizeof(dp));
        for(ll i=1;i<n;i++){
            if(gcd(num[i],num[i+1])!=1)
                dp[i][i+1]=1;
        }
        for(ll l=2;l<=n;l+=2){
            for(ll i=1;i+l-1<=n;i++){
                ll j=i+l-1;
                if(gcd(num[i],num[j])!=1&&dp[i+1][j-1]==1)
                    dp[i][j]=1;
                if(gcd(num[i],num[i+1])!=1&&dp[i+2][j]==1)
                    dp[i][j]=1;
                if(gcd(num[j-1],num[j])!=1&&dp[i][j-2]==1)
                    dp[i][j]=1;
            }
        }
        Dp();
    }
    
    int main(){
        ll t;
        scanf("%lld",&t);
        while(t--){
            scanf("%lld",&n);
            memset(sum,0,sizeof(sum));
            for(ll i=1;i<=n;i++){
                scanf("%lld",&num[i]);
    
            }
            for(ll i=1;i<=n;i++){
                scanf("%lld",&val[i]);
                sum[i]=sum[i-1]+val[i];
            }
            query();
            cout<<out[1][n]<<endl;
        }
    }

    odd-even number

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5898

    题意:

    给你一个区间,问你这个区间中满足连续的偶数的位数为奇数,连续的奇数的位数是偶数的个数

    题解:

    设dp[i][j][k][l]为考虑当前第i位,上一位的奇偶性为j,已经连续了k位,是否有前导零

    然后记忆化搜就行了

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    using namespace std;
    typedef long long ll;
    
    int dig[20],len;
    ll dp[20][2][20][2];
    
    ll dfs(int pos,int pre=0,int ln=0,bool inf=1,bool ze=1)//pre 1为奇;0为偶,ze是否有前导零 1代表有,0没有;inf判断当前位最大值 1表示最大值为dig[pos]
    {
        if(!pos)//搜索完成时,根据连续的长度和当前连续的是奇数还是偶数判断反回1还是0
        {
            if(pre)return (ln&1)==0;
            else return ln&1;
        }
        if(!inf&&dp[pos][pre][ln][ze]!=-1)return dp[pos][pre][ln][ze];
        int en=inf?dig[pos]:9;//当前位最大放几
        //cout<<pos<<" "<<en<<endl;
        ll ans=0;
        F(i,0,en)
        {
            if(i&1)//当前数为奇数
            {
                //cout<<i<<" "<<"?"<<endl;
                if(ze)//上一位为0的情况
                {
                    ans+=dfs(pos-1,1,1,inf&&i==en,0);
                }
                else if(pre==0)//上一位为偶数,因为本次是奇数,所以ln从1开始
                {
                    if(ln&1)
                        ans+=dfs(pos-1,1,1,inf&&i==en,ze);
                }
                else if(pre==1)//上一位为奇数
                {
                    ans+=dfs(pos-1,1,ln+1,inf&&i==en,ze);
                }
            }
            else//偶数
            {
                if(ze)
                {
                    if(i==0)ans+=dfs(pos-1,0,0,inf&&i==en,1);
                    else ans+=dfs(pos-1,0,1,inf&&i==en,0);
                }
                else if(pre==0)ans+=dfs(pos-1,0,ln+1,inf&&i==en,ze);
                else if(pre==1)
                {
                    if((ln&1)==0)ans+=dfs(pos-1,0,1,inf&&i==en,ze);
                }
            }
        }
        if(!inf)dp[pos][pre][ln][ze]=ans;
        return ans;
    }
    
    int main(){
        int t,ic=1;ll l,r;
        scanf("%d",&t);
        while(t--)
        {
            memset(dp,-1,sizeof(dp));
            scanf("%lld%lld",&l,&r),l--;
            for(len=0;l;l/=10)dig[++len]=l%10;//将数转化为字符串但是逆序存放
            ll tp=dfs(len);
            for(len=0;r;r/=10)dig[++len]=r%10;
            printf("Case #%d: %lld
    ",ic++,dfs(len)-tp);
        }
        return 0;
    }

    List wants to travel

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5893

    题目大意:
    给出一棵树上的两种操作:
    1.将节点a到节点b路径上的边权全部修改为c;
    2.查询节点a到节点b路径上边权的段数。
     
    解题思路:
    先进行树链剖分,将问题转化到区间上,之后统计段数是经典的线段树区间合并问题。修改操作直接修改就好了,对于查询操作,需要注意链的合并,可以先求一次a与b的LCA,之后将路径分为两段分别查询,最后看拼接处权值是否相同。
    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 40010
    const int MAXM = 50050 ;
    struct Edge
    {
        int to,next;
        int len;
    }e[MAXM*2];
    int first[maxn];
    int top[maxn];//top[v]表示v所在的重链的顶端节点
    int fa[maxn]; //父亲节点
    int deep[maxn];//深度
    int num[maxn];//num[v]表示以v为根的子树的节点数
    int p[maxn];//p[v]表示v与其父亲节点的连边在线段树中的位置
    int fp[maxn];//和p数组相反
    int son[maxn];//重儿子
    int pos,tot,Lc,Rc;
    int c[maxn],a[maxn];
    void add(int u,int v,int len)
    {
        tot++;
        e[tot].next=first[u];e[tot].to=v;e[tot].len=len;
        first[u]=tot;
    }
    void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
    {
        deep[u] = d;
        fa[u] = pre;
        num[u] = 1;
        for(int i = first[u];i != -1; i = e[i].next)
        {
            int v = e[i].to;
            if(v != pre)
            {
                a[v]=e[i].len;
                dfs1(v,u,d+1);
                num[u] += num[v];
                if(son[u] == -1 || num[v] > num[son[u]])
                    son[u] = v;
            }
        }
    }
    void getpos(int u,int sp) //第二遍dfs求出top和p
    {
        top[u] = sp;
        if(son[u] != -1)
        {
            p[u] = ++pos;
            fp[p[u]] = u;
            c[pos]=a[u];
            getpos(son[u],sp);
        }
        else
        {
            p[u] = ++pos;
            fp[p[u]] = u;
            c[pos]=a[u];
            return;
        }
        for(int i = first[u] ; i != -1; i = e[i].next)
        {
            int v = e[i].to;
            if(v != son[u] && v != fa[u])
                getpos(v,v);
        }
    }
    
    //线段树
    struct Node
    {
        int l,r;
        int num;
        int cl,cr;
        int flag;
    }segTree[MAXM*3];
    void push_up(int i)
    {
        segTree[i].cl=segTree[i<<1].cl;
        segTree[i].cr=segTree[i<<1|1].cr;
        segTree[i].num=segTree[i<<1].num+segTree[i<<1|1].num;
        if(segTree[i<<1].cr==segTree[i<<1|1].cl)
            segTree[i].num--;
    }
    void push_down(int i){
        if(segTree[i].flag==1){
            segTree[i<<1].flag=segTree[i<<1|1].flag=1;
            segTree[i<<1].cl=segTree[i<<1].cr=segTree[i<<1|1].cl=segTree[i<<1|1].cr=segTree[i].cl;
            segTree[i<<1].num=segTree[i<<1|1].num=1;
            segTree[i].flag=-1;
        }
    }
    void build(int i,int l,int r)
    {
        segTree[i].l = l;
        segTree[i].r = r;
        segTree[i].flag=-1;
        if(l == r){
            segTree[i].cl=segTree[i].cr=c[l];
            segTree[i].num=1;
            return ;
        }
        int mid = (l+r)/2;
        build(i<<1,l,mid);
        build((i<<1)|1,mid+1,r);
        push_up(i);
    }
    
    void update(int l,int r,int color,int i)
    {
        if(segTree[i].l == l && segTree[i].r == r)
        {
            segTree[i].num=1;
            segTree[i].flag=1;
            segTree[i].cl=segTree[i].cr=color;
            return;
        }
        push_down(i);
        int mid = (segTree[i].l + segTree[i].r)/2;
        if(r <= mid)update(l,r,color,i<<1);
        else if(l>mid) update(l,r,color,i<<1|1);
        else{
            update(l,mid,color,i<<1);
            update(mid+1,r,color,i<<1|1);
        }
        push_up(i);
    }
    int query(int l,int r,int i,int L,int R)  
    {
        if(segTree[i].l == L )
            Lc=segTree[i].cl;
        if(segTree[i].r==R)
            Rc=segTree[i].cr;
        if(segTree[i].l==l&&segTree[i].r==r){
            return segTree[i].num;
        }
        push_down(i);
        int mid = (segTree[i].l + segTree[i].r)/2;
        if(r <= mid)return query(l,r,i<<1,L,R);
        else if(l > mid)return query(l,r,i<<1|1,L,R);
        else{
            int num=query(l,mid,i<<1,L,R)+query(mid+1,r,i<<1|1,L,R);
            if(segTree[i<<1].cr==segTree[i<<1|1].cl)
                num--;
            return num;
        }
    }
    int solve(int u,int v)
    {
        int f1 = top[u], f2 = top[v];
        int tmp = 0;
        int pre1=-1,pre2=-1;
        while(f1 != f2)
        {
            if(deep[f1] < deep[f2])
            {
                swap(pre1,pre2);
                swap(f1,f2);
                swap(u,v);
            }
            tmp +=query(p[f1],p[u],1,p[f1],p[u]);
            if(pre1==Rc)
                tmp--;
            pre1=Lc;
            u = fa[f1]; f1 = top[u];
        }
        if(u!=v){
            if(deep[u]<deep[v]){
                swap(pre1,pre2);
                swap(u,v);
            }
            tmp+=query(p[son[v]],p[u],1,p[son[v]],p[u]);
            if(pre1!=-1 && Rc==pre1)tmp--;
            if(pre2!=-1 && Lc==pre2)tmp--;
        }
        else if(pre1==pre2)
            tmp--;
        return tmp;
    }
    
    void gengxin(int u,int v,int value)
    {
        int f1,f2;
        f1=top[u];f2=top[v];
        while(f1!=f2){
            if(deep[f1]<deep[f2]){
                swap(f1,f2);swap(u,v);
            }
            update(p[f1],p[u],value,1);
            u=fa[f1];
            f1=top[u];
        }
        if(deep[u]<deep[v]){
            swap(u,v);
        }
        if(u!=v){
            update(p[son[v]],p[u],value,1);
        }
    }
    int main()
    {
        int i,j,n,m,u,v,f,g,h,w;
        char s[10];
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            memset(first,-1,sizeof(first));
            memset(son,-1,sizeof(son));
            pos=0;tot=0;
            for(i=1;i<=n-1;i++){
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);add(v,u,w);
            }
            dfs1(1,0,1);
            getpos(1,1);
    
            build(1,1,pos);
            for(i=1;i<=m;i++){
                scanf("%s",s);
                if(s[0]=='Q'){
                    scanf("%d%d",&f,&g);
                    printf("%d
    ",solve(f,g));
                }
                else{
                    scanf("%d%d%d",&f,&g,&h);
                    gengxin(f,g,h);
                }
            }
    
        }
        return 0;
    }
  • 相关阅读:
    《失业的程序员》(十):分歧的产生
    《失业的程序员》(九):创业就是一场戏
    使用MySQL处理百万级以上数据时,不得不知道的几个常识
    《失业的程序员》(八):创业的要素
    《失业的程序员》(七):梦想和胸襟-正文
    《失业的程序员》(六):加班
    《失业的程序员》(五):商战之前
    《失业的程序员》(四):关于猪刚烈
    《失业的程序员》(二):酒后的第一桶金
    《失业的程序员》(三):口才帝和表情帝
  • 原文地址:https://www.cnblogs.com/137033036-wjl/p/6031576.html
Copyright © 2020-2023  润新知