每次都被虐的感受-.-是那么的酸爽----
Problem C: F
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 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
Sample Output
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 MBSubmit: 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
Sample Output
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 MBSubmit: 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
Sample Output
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 MBSubmit: 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
Sample Output
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 MBSubmit: 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
Sample Output
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 MBSubmit: 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
Sample Output
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;
}