2015 Multi-University Training Contest 3(HDOJ5316、5317、5319、5326)

官方题解:http://blog.sina.com.cn/duoxiao2015


这是放暑假后的第一场,上场在赶课设的deadline,就没去,然后队友爆零了(貌似他们晚上有考试,就只写了几小时的样子)

这场做第二题时脑抽,被卡烦了,就把写一半的代码扔给队友继续写


HDOJ5316

题意:n个数字,m个操作

操作0 a b,在[a,b]区间选出一个子序列,该子序列的所有相邻元素原位置奇偶不同,输出满足条件的子序列的各个数字之和最大值,子序列不能为空

操作1 a b,把位置a的数改成b

思路:明显线段树题,维护奇奇、奇偶、偶奇、偶偶的最大值就好

注意:负数的情况

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAX = 100010;
const LL INF = (LL)1<<60;
int r, c, m;
int op, x1, x2, y1, y2, x, v,T,Q,n,a,b;
int data[MAX];

struct tree{
    LL s[2][2];//节点中需维护的东西
};

tree res;

struct IntervalTree
{
    tree setv[MAX<<2];

    void build(int num,int l,int r){//建树
        int lc = num*2, rc = num*2+1;
        //setv[num] = -1;
        for(int i=0;i<2;i++){//初始化每个节点
            for(int j=0;j<2;j++){
                setv[num].s[i][j]=INF;
            }
        }
        if(l == r){//初始化叶子节点值
            //setv[num] = data[l];
            if(l&1)setv[num].s[1][1]=data[l];
            else setv[num].s[0][0]=data[l];
            return ;
        }
        int mid = (l+r)>>1;
        build(lc, l, mid);
        build(rc, mid+1, r);
        maintain(num,l,r);//如果需要向上合并就加
    }
    /*
    void pushdown(int num)//向下分
    {
        int lc = num*2, rc = num*2+1;
        if(setv[num] >= 0)
        {
            setv[lc] = setv[rc] = setv[num];
            setv[num] = -1;
        }
    }*/

    void maintain(int num,int l,int r){//向上合并
        int lc = num*2, rc = num*2+1;
        if(r>l){//合并更新非叶子节点值
            for(int i=0;i<2;i++){
                for(int j=0;j<2;j++){
                    int f=1;//未更新
                    if(setv[lc].s[i][j]!=INF){
                        if(f){setv[num].s[i][j]=setv[lc].s[i][j];f=0;}
                        else setv[num].s[i][j]=max(setv[num].s[i][j],setv[lc].s[i][j]);
                    }
                    if(setv[rc].s[i][j]!=INF){
                        if(f){setv[num].s[i][j]=setv[rc].s[i][j];f=0;}
                        else setv[num].s[i][j]=max(setv[num].s[i][j],setv[rc].s[i][j]);
                    }
                    if(setv[lc].s[i][0]!=INF&&setv[rc].s[1][j]!=INF){
                        if(f){setv[num].s[i][j]=setv[lc].s[i][0]+setv[rc].s[1][j];f=0;}
                        else setv[num].s[i][j]=max(setv[num].s[i][j],setv[lc].s[i][0]+setv[rc].s[1][j]);
                    }
                    if(setv[lc].s[i][1]!=INF&&setv[rc].s[0][j]!=INF){
                        if(f){setv[num].s[i][j]=setv[lc].s[i][1]+setv[rc].s[0][j];f=0;}
                        else setv[num].s[i][j]=max(setv[num].s[i][j],setv[lc].s[i][1]+setv[rc].s[0][j]);
                    }
                }
            }
        }
        //else就是叶子节点
        //更新节点值
    }

    void update(int num, int l, int r)
    {
        int lc = num*2, rc = num*2+1;
        if(y1 <= l && r <= y2)
        {
            if(l&1)setv[num].s[1][1]=v;
            else setv[num].s[0][0]=v;
        }
        else
        {
            //pushdown(num);//加了这句一般要再加两句maintain
            int mid = l+(r-l)/2;
            if(y1 <= mid) update(lc, l, mid);
            if(y2 > mid) update(rc, mid+1, r);
        }
        maintain(num,l,r);//
    }

    void query(int num,int l,int r){//本题的查找结果
        int lc = num*2, rc = num*2+1;
        if(y1 <= l && r <= y2)
        {
            tree res2;
            for(int i=0;i<2;i++){
                for(int j=0;j<2;j++){
                    res2.s[i][j]=res.s[i][j];
                }
            }
            for(int i=0;i<2;i++){
                for(int j=0;j<2;j++){
                    int f=1;//未更新
                    if(res2.s[i][j]!=INF){
                        if(f){res.s[i][j]=res2.s[i][j];f=0;}
                        else res.s[i][j]=max(res.s[i][j],res2.s[i][j]);
                    }
                    if(setv[num].s[i][j]!=INF){
                        if(f){res.s[i][j]=setv[num].s[i][j];f=0;}
                        else res.s[i][j]=max(res.s[i][j],setv[num].s[i][j]);
                    }
                    if(res2.s[i][0]!=INF&&setv[num].s[1][j]!=INF){
                        if(f){res.s[i][j]=res2.s[i][0]+setv[num].s[1][j];f=0;}
                        else res.s[i][j]=max(res.s[i][j],res2.s[i][0]+setv[num].s[1][j]);
                    }
                    if(res2.s[i][1]!=INF&&setv[num].s[0][j]!=INF){
                        if(f){res.s[i][j]=res2.s[i][1]+setv[num].s[0][j];f=0;}
                        else res.s[i][j]=max(res.s[i][j],res2.s[i][1]+setv[num].s[0][j]);
                    }
                }
            }
        }
        else
        {
            //pushdown(num);
            int mid = l+r>>1;
            if(y1 <= mid) query(lc, l, mid);
            if(y2 > mid) query(rc, mid+1, r);
        }
    }
}pt;

template <class T>
inline bool read(T &ret) {
   char c; int sgn;
   if(c=getchar(),c==EOF) return 0; //EOF
   while(c!='-'&&(c<'0'||c>'9')) c=getchar();
   sgn=(c=='-')?-1:1;
   ret=(c=='-')?0:(c-'0');
   while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
   ret*=sgn;
   return 1;
}

int main()
{
#ifdef DEBUG
   freopen("CBin.txt","r",stdin);
   //freopen("CBout.txt","w",stdout);
#endif
    cin>>T;
    while(T--)
    {
        read(n);read(Q);
        memset(pt.setv, -1, sizeof(pt.setv));
        for(int i = 1; i <= n; ++i)
        {
            read(data[i]);
        }
        pt.build(1,1,n);
        while(Q--)
        {
            read(op);read(a);read(b);
            if(op){
                y1=a;
                y2=a;
                v=b;
                pt.update(1, 1, n);
            }
            else {
                y1=a;
                y2=b;
                for(int i=0;i<2;i++){
                    for(int j=0;j<2;j++){
                        res.s[i][j]=INF;
                    }
                }
                pt.query(1, 1, n);
                LL r;
                int f=1;
                for(int i=0;i<2;i++){
                    for(int j=0;j<2;j++){
                        if(res.s[i][j]!=INF){
                            if(f){r=res.s[i][j];f=0;}
                            else r=max(r,res.s[i][j]);
                        }
                    }
                }
                cout<<r<<"\n";
            }
        }
    }
    return 0;
}



HDOJ5317

题意:求区间[l,r]中maxGCD(F(i),F(j)),(L≤i<j≤R),其中F(i)表示i的质因子有多少种

思路:由于测试数据有百万组,所以要用O(1)的方法,容易发现结果最大是7

第一步:先用修改过的素数筛法在伪线性的时间预处理所有的F(i)

第二步:接着再预处理每个数为起点对应的每个maxgcd值的最小范围(这步不是我写的,思路是赛后想到的,队友可能不是按这思路AC的)

接下来就可以O(1)的查找了

代码第二步是队友写的,就不贴了


HDOJ5319

题意:给n行的图,RGB代表3种颜色,点代表空白,现在让你画出这个图,求最少几笔,R只能斜着‘\’这样画,B只能斜着‘/’这样画,RB都画过的格子是G,一个格子不能被相同画笔画两次

思路:G必定被RB划过,可以先把G画好,再画剩下的

注意:给的是n行的图,不是n*n的图

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN=50+10;

int T,n,m;
int g[MAXN][MAXN],now[MAXN][MAXN];
int sum;
string c;

void init(){
    m=0;
    for (int i=0;i<n;++i){
        cin>>c;
        m=c.size();
        for (int j=0;j<m;++j){
            if(c[j]=='.')g[i][j]=0;
            if(c[j]=='R')g[i][j]=1;
            if(c[j]=='B')g[i][j]=2;
            if(c[j]=='G')g[i][j]=3;
        }
    }
    sum=0;
    memset(now,0,sizeof(now));
}

void drawR(int x,int y){
    sum++;
    //int k=x-y;
    for (int i=0;x-i>=0&&y-i>=0&&x-i<n&&y-i<m;++i){
        if((g[x-i][y-i]==3&&(now[x-i][y-i]==0||now[x-i][y-i]==2))||(g[x-i][y-i]==1&&(now[x-i][y-i]==0)))now[x-i][y-i]+=1;
        else break;
    }
    for (int i=-1;x-i>=0&&y-i>=0&&x-i<n&&y-i<m;--i){
        if((g[x-i][y-i]==3&&(now[x-i][y-i]==0||now[x-i][y-i]==2))||(g[x-i][y-i]==1&&(now[x-i][y-i]==0)))now[x-i][y-i]+=1;
        else break;
    }
}

void drawB(int x,int y){
    sum++;
    for (int i=0;x-i>=0&&y+i>=0&&x-i<n&&y+i<m;++i){
        if((g[x-i][y+i]==3&&(now[x-i][y+i]==0||now[x-i][y+i]==1))||(g[x-i][y+i]==2&&(now[x-i][y+i]==0)))now[x-i][y+i]+=2;
        else break;
    }
    for (int i=-1;x-i>=0&&y+i>=0&&x-i<n&&y+i<m;--i){
        if((g[x-i][y+i]==3&&(now[x-i][y+i]==0||now[x-i][y+i]==1))||(g[x-i][y+i]==2&&(now[x-i][y+i]==0)))now[x-i][y+i]+=2;
        else break;
    }
}

void work(){
    for (int i=0;i<n;++i){
        for (int j=0;j<m;++j){
            if(g[i][j]==3&&now[i][j]==0){drawR(i,j);drawB(i,j);}
            if(g[i][j]==3&&now[i][j]==1){drawB(i,j);}
            if(g[i][j]==3&&now[i][j]==2){drawR(i,j);}
        }
    }

    for (int i=0;i<n;++i){
        for (int j=0;j<m;++j){
            if(g[i][j]==1&&now[i][j]==0){drawR(i,j);}
            if(g[i][j]==2&&now[i][j]==0){drawB(i,j);}
        }
    }
}

int main(){
#ifdef DEBUG
   freopen("CBin.txt","r",stdin);
   //freopen("CBout.txt","w",stdout);
#endif
    cin>>T;
    while(T--){
        cin>>n;
        init();
        work();
        cout<<sum<<"\n";
    }
    return 0;
}


HDOJ5326

题意:给一颗大小为n的树,问该树中节点的后代数量恰好为k的有几个

思路:水题,我直接套了我以前写的树形DP的模版

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
//typedef long long LL;
const int MAXN = 110;
int n,e,s,m,k;
int dp[MAXN];
bool f[MAXN];
int sum;
struct Edge{
    int next;
    Edge();
    Edge(int b){next=b;}
};
vector<Edge>dv[MAXN];
void init()
{
    for(int i=0;i<=n;i++) dv[i].clear();
    memset(f,0,sizeof(f));
    memset(dp,0,sizeof(dp));
    sum=0;
}
void dfs(int p,int fa)
{
    int son;
    dp[p]+=1;
    for(int i=0;i<dv[p].size();i++)
    {
        son=dv[p][i].next;
        if(son^fa){
            dfs(son,p);
            dp[p]+= dp[son];
            //cout<<son<<p<<" "<<dv[p][i].val<<" "<<dp[son]<<" "<<res<<"\n";
        }

    }
        if(dp[p]==k+1)sum++;
}
int main()
{
#ifdef DEBUG
   freopen("CBin.txt","r",stdin);
   //freopen("CBout.txt","w",stdout);
#endif
    while(~scanf("%d%d",&n,&k))
    {
        init();

        for(int i=1;i<n;i++)
        {
            int b,c;
            scanf("%d%d",&b,&c);
            dv[c].push_back(Edge(b));
            dv[b].push_back(Edge(c));
            f[c]=1;
        }
        int root;
        for(int i=1;i<=n;i++)
        {
            if(!f[i]){root=i;break;}
        }
        dfs(root,0);
        cout<<sum<<"\n";
        //printf("%d\n",max(dp[root][0],dp[root][1]));
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值