0
点赞
收藏
分享

微信扫一扫

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


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





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;
}





举报

相关推荐

0 条评论