zzuli oj 多校训练(三)【树链剖分--树状数组---STL----二分图----二分----DP】

20 篇文章 0 订阅
14 篇文章 0 订阅

每次都被虐的感受-.-是那么的酸爽----




Problem C: F

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 213   Solved: 14

Submit Status Web Board

Description

给一颗树,有n个结点,编号为1到n,1为根节点,有两种操作,1 x y把x结点权值加y,2 x查询x到根节点所有结点的权值和.
每个结点权值初始化为0。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行为两个整数n,m代表结点个数和操作次数。
接下来n-1行,每行两个整数a,b,表示a结点和b结点有一条边。
接下来m行每行一个操作格式如上。
0<=n<=50000 ,0<=m<=50000,0<=y<=50000

Output

对于每组查询输出一个整数表示结果

Sample Input

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

Sample Output

0
3
2

HINT


树的近似剖分--与LCA的原理很像(回溯法--)

LCA原理推导:hdoj 2586

形成一个链--线型就可以用树状数组了


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define MA 60000
#define lowit(x) (x&-x)
LL zhuang[2*MA];
int ru[MA],chu[MA];
int n,m,head[MA],kp;
bool fafe[MA];
struct node{
    int to,next;
}edge[MA*2];
void dfs(int xx)
{
    ru[xx]=kp++;
    fafe[xx]=false;
    for (int k=head[xx];k!=-1;k=edge[k].next)
    {
        if (fafe[edge[k].to])
            dfs(edge[k].to);
    }
    chu[xx]=kp++;
    return ;
}
void ADD(int xx,int yy)
{
    for (;xx<=n;xx+=lowit(xx))
        zhuang[xx]+=yy;
}
LL Query(int xx)
{
  LL lp=0;
  for (;xx;xx-=lowit(xx))
    lp+=zhuang[xx];
  return lp;
}
void slove()
{
    scanf("%d%d",&n,&m);
    int a,b,c;
    memset(fafe,true,sizeof(fafe));
    memset(zhuang,0,sizeof(zhuang));
    for (int i=0;i<=n;i++)
        head[i]=-1;
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        edge[i*2-1].to=b;
        edge[i*2-1].next=head[a];
        head[a]=i*2-1;
        edge[i*2].to=a;
        edge[i*2].next=head[b];
        head[b]=i*2;
    }
    kp=1;n*=2;
    dfs(1);
 /*   for (int i=1;i<=n;i++)-----自坑
        printf("%d %d\n",ru[i],chu[i]);*/
    for (int i=0;i<m;i++)
    {
        scanf("%d",&c);
        if (c==1)
        {
            scanf("%d%d",&a,&b);
            if (a<0||a>n/2) continue;
            ADD(ru[a],b);
            ADD(chu[a],-b);
        }
        else
        {
            scanf("%d",&a);
            printf("%lld\n",Query(ru[a]));
        }
    }
}
int main()
{
    int t;scanf("%d",&t);
    while (t--)
        slove();
    return 0;
}






Problem D: E

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 88   Solved: 10

Submit Status Web Board

Description

晴天有非常严重的选择恐惧症,每次吃饭前他都在纠结到底吃什么。。今天又到了吃饭的时候了。

重光:我给你一个包含n个不同整数的序列a,如果它所有连续子序列的价值和是素数咱们就吃米,不然就吃面。

定义一个序列的价值为序列中所有元素的最小值。

晴天:这不是分分钟给你算出来。

嗯...十分钟过去了,晴天选择死亡。

这个任务就交给你啦。

算出所有连续子序列的价值和。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行包含一个整数n,表示序列a的元素个数。
接下来一行包含n个整数,表示序列a。
0<=n<=50000,1<=ai<=50000。

Output

对于每组数据输出一个整数,表示序列a的所有连续子序列的价值和。

Sample Input

1
3
1 2 3

Sample Output

10

D题;好吧--可以直接用vector+二分====我一直在用线段树---右边的不会求了--(还想反建树----)-------好吧--我个渣渣


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define LL long long
#define MA 60000
struct node{
    int hao,zhi;
}dian[MA];
bool cmp(node xx,node yy)
{
    return xx.zhi<yy.zhi;
}
int n;
void slove()
{
	scanf("%d",&n);
	for (int i=0;i<n;i++)
    {
        scanf("%d",&dian[i].zhi);
        dian[i].hao=i+2;
    }
    LL ans=0,lp;
    int zuo,you;
    sort(dian,dian+n,cmp);
    vector <int > ve;
    ve.push_back(1);
    ve.push_back(n+2);
    for (int i=0;i<n;i++)
    {
        int k=dian[i].hao;
        int kp=upper_bound(ve.begin(),ve.end(),k)-ve.begin();
        lp=(ve[kp]-k)*(k-ve[kp-1]);
        ans+=lp*dian[i].zhi;
        ve.insert(ve.begin()+kp,k);
    }
    printf("%lld\n",ans);
}
int main()
{
	int t;scanf("%d",&t);
	while (t--)
	slove();
	return 0;
}


Problem E: G

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 353   Solved: 84

Submit Status Web Board

Description

晴天也来寻宝啦,有一个m层的宝塔,只能从第一层开始一层一层的往上走,每层都有一个门,你需要用钥匙来打开门才能继续走,现在晴天有n把钥匙,编号为0-n-1,然后他要开始寻宝了。没有特殊技能怎么好意思出来寻宝呢,他现在有两个天赋技能,他知道第i层的门可以用编号为a和b的钥匙打开(可能a等于b呦),然后他还可以在进入宝塔前把门的顺序任意调换一次,也就是说比如可以把m层原来的1 2 3 ..m,换为 m ...3 2 1.晴天想知道他最多能拿到多少层的宝物。

Input

第一行一个整数t表示有多少组测试实例

每组数据第一行为两个整数n,m分别表示有多少个钥匙,有多少层。

接下来m行,每行两个数字x,y,第i行表示第i层的门可以用标号x或y的钥匙打开。

(n,m<=1000)

Output

输出一个整数表示最多可以上多少层。

Sample Input

1
3 4
0 1
0 1
0 1
1 2

Sample Output

3

HINT

在样例中,在进入宝塔前,将门的顺序换为4 1 2 3.然后前三层分别使用2 0 1三把钥匙拿到前三层的宝物


赤裸裸的二分图我竟然都不会---啊啊啊啊---下次雨神讲题我要去哭哭


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int shu[1010][2];
int fer[1010];
bool guo[1010];
int ke(int xx)
{
    for (int i=0;i<2;i++)
    {
        if (shu[xx][i]>=n||!guo[shu[xx][i]])
            continue;
        int a=shu[xx][i];
        guo[a]=false;
        if (!fer[a])
        {
            fer[a]=xx;
            return true;
        }
        if (ke(fer[a]))
        {
            fer[a]=xx;
            return true;
        }
    }
    return false;
}
void slove()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
        scanf("%d%d",&shu[i][0],&shu[i][1]);
    memset(fer,0,sizeof(fer));
    int s=0;
    for (int i=1;i<=m;i++)
    {
        memset(guo,true,sizeof(guo));
        if (ke(i))
        s++;
    }
    printf("%d\n",s);
}
int main()
{
    int t;scanf("%d",&t);
    while (t--)
        slove();
    return 0;
}






Problem F: D

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 178   Solved: 38

Submit Status Web Board

Description

晴天想把一个包含n个整数的序列a分成连续的若干段,且和最大的一段的值最小,但他有强迫症,分的段数不能超过m段,然后他就不会分了。。。他想问你这个分出来的和最大的一段的和最小值是多少?

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行为两个整数n,m分别代表序列的长度和最多可分的段数。
接下来一行包含n个整数表示序列。
0<=n<=50000 1<=m<=n,0<=ai<=10000。

Output

输出一个整数表示和最大的一段的最小值。

Sample Input

1
3 2
1 3 5

Sample Output

5

HINT

1 3 5 分成一段可以为1 3 5和为9,分成两段可以为1,3 5或者1 3,5,和最大的一段值分别为8,5,所以答案为5



用二分判断每个长度是否可行

代码:

#include<cstdio>
#include<cstring> 
#include<algorithm> 
using namespace std;
int shu[60000];
int n,m;
bool zhao(int xx)
{
    int lp=0,s=0;
    for (int i=0;i<n;i++)
    {
        if (s+shu[i]>xx)
        {
            lp++;
            s=shu[i];
        }
        else
        s+=shu[i];
    }
    if (s>0) lp++;
    if (lp<=m)
    return true;
    return false;
}
void slove()
{
    scanf("%d%d",&n,&m);
    int l=0,M,r=0;
    for (int i=0;i<n;i++)
    {
        scanf("%d",&shu[i]);
        r+=shu[i];
        l=max(l,shu[i]);
    }
    int ans=0;
    while (l<=r)
    {
         
        M=(l+r)/2;//printf("%d  %d\n",l,r);
        if (zhao(M))
        {
            ans=M;
            r=M-1;
        }
        else
        l=M+1; 
    }
    printf("%d\n",ans);
}
int main()
{
    int t;scanf("%d",&t);
    while (t--)
    slove();
    return 0;
}



Problem H: B

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 189   Solved: 10

Submit Status Web Board

Description

给定一个长度为n的数字序列a,从中选取一个长为m的子序列b满足 b[i]&b[i-1]!=0 (2<=i<=m)
求最大的m。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入一个整数n代表a序列的长度,接下来一行输入n个正整数表示ai(0<i<=n)。
1<=t<=20,0<=n<=100000,0<=ai<=1e9。

Output

一个整数表示最大的m。

Sample Input

1
3
1 1 1

Sample Output

3

HINT


二进制+动态规划

以每个数结尾的都是前面与它可&的中最大的那个--


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int shu[40];
int s;
void suan(int xx)
{
    int lp=0;
    int qu[35],kp=0;
    while (xx)
    {
        if (xx%2==1)
        {
            qu[kp++]=lp;
        }
        lp++;
        xx/=2;
    }
    int ma=0;
    for (int i=0;i<kp;i++)
    {
        ma=max(ma,shu[qu[i]]);
    }
    ma++;
    s=max(s,ma);
    for (int i=0;i<kp;i++)
    {
        shu[qu[i]]=ma;
    }
    return;
}
int main()
{
    int t;scanf("%d",&t);
    while (t--)
    {
        int n;scanf("%d",&n);
        memset(shu,0,sizeof(shu));
        int a;s=0;
        while (n--)
        {
            scanf("%d",&a);
            suan(a);
        }
        printf("%d\n",s);
    }
    return 0;
}




Problem I: A

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 45   Solved: 7

Submit Status Web Board

Description

给定一个4*4的01棋盘,1代表棋子,0代表空格,棋子1每次可以移动到相邻上下左右四个位置的空格。
然后再给定你目标棋盘,问你最少在多少步能把当前棋盘变成目标棋盘状态。

Input

第一行输入一个整数t,代表有t组测试数据。
接下来给出只有0和1的4*4的当前棋盘和4*4的目标棋盘,中间有一个空行。

Output

输出一个整数表示最小的步数,若不能到达输出-1.

Sample Input

1
0001
0011
1100
1111

1011
1101
0000
1101

Sample Output

8

HINT


感觉代码不对--

此题甚晕--

也可能对--(因为从上到下,从左到右??)


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char ch1[5][5],ch2[5][5];
bool fafe1[5][5],fafe2[5][5];
void slove()
{
    for (int i=0;i<4;i++)
    scanf("%s",ch1[i]);
    for (int i=0;i<4;i++)
    scanf("%s",ch2[i]);
    memset(fafe1,false,sizeof(fafe1));
    memset(fafe2,false,sizeof(fafe2));
    int a=0,b=0;
    for (int i=0;i<4;i++)
    {
        for (int j=0;j<4;j++)
        if (ch1[i][j]=='1'&&ch2[i][j]=='0')
        {
            fafe1[i][j]=true;
            a++;
        }
        else if (ch1[i][j]=='0'&&ch2[i][j]=='1')
        {
            fafe2[i][j]=true;
            b++;
        }
    }
    if (a!=b)
    {
        printf("-1\n");
        return ;
    }
    if (a==0&&b==0)
    {
        printf("0\n");
        return ;
    }
    int ans=0;
    for (int i=0;i<4;i++)
    for (int j=0;j<4;j++)
    {
        if (fafe1[i][j])
        {
            int ii,jj,s=9999;
            for (int k=0;k<4;k++)
            {
                for (int l=0;l<4;l++)
                {
                    if (fafe2[k][l])
                    {
                        if (abs(k-i)+abs(l-j)<s)
                        {
                            s=abs(k-i)+abs(l-j);
                            ii=k;jj=l;
                        }
                    }
                }
            }
            ans+=s;
            fafe2[ii][jj]=false;
            fafe1[i][j]=false;
        }
    }
    printf("%d\n",ans);
    return ;
}
int main()
{
    int t;scanf("%d",&t);
    while (t--)
    slove();
    return 0;
}




评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值