地球发动机(earth)
【题目描述】
“啊,地球,我的流浪地球……”
——《流浪地球》
在一条直线上,从左到右排列着n台地球发动机,每台发动机有着固定的位置坐标Ai和功率Pi,保证Ai<Ai+1。此外,由于地球发动机的特性,每台发动机还有一个参数Xi,如果一台发动机运行,则坐标范围在[Ai,Ai+Xi]的其它发动机就无法运行。现在你想让正在运行的发动机总功率最大,请输出这个总功率。
【输入数据】
第一行一个整数n,意义如上所述。
接下来n行,每行三个整数Ai,Pi,Xi,意义如题面所述。
【输出数据】
一行一个整数,表示可能的最大功率。
【样例输入】
4
2 5 1
5 4 3
8 10 3
9 2 2
【样例输出】
15
【数据范围】
对于20%的数据,n≤10,0<Ai,Pi,Xi≤10;
对于50%的数据,n≤2000,0<Ai,Pi,Xi≤105;
对于100%的数据,n≤105,0<Ai,Pi,Xi≤109。
图(graph)
【题目描述】
小H有一张n个点,m条边的无向连通图,他想从图中选出一些边,保证通过这些边a和b连通,c和d连通,同时选出的边数尽量少。
【输入数据】
第一行两个整数n,m,表示图中的边数和点数。
第二行四个整数a,b,c,d,意义如题面所述。
接下来m行,每行两个整数a,b,表示点a和点b间有一条边。
【输出数据】
一行一个整数,表示最少需要选出的边数。
【样例输入】
5 8
3 4 1 3
2 1
3 2
4 3
5 3
4 2
1 4
5 4
2 1
【样例输出】
2
【数据范围】
对于所有数据,保证1≤a,b,c,d≤n;
对于10%的数据,0<n,m≤20;
对于30%的数据,0<n,m≤300;
对于60%的数据,0<n≤300;
对于100%的数据,0<n,m≤3000。
树(tree)
【题目描述】
小H有一棵n个节点的树T,每个节点上有一个非负整数Ai,他想知道所有距离不超过k的点对 ( x , y ) (x<y)上数的亦或值的和。
【输入数据】
第一行两个正整数n,k,表示树上的节点数和给定的距离k。
第二行n个非负整数,第i个数表示第i个节点上的数是Ai。
接下来n-1行,每行两个正整数u,v,表示节点u和节点v之间有一条边。保证输入是一棵树。
【输出数据】
一行一个整数,表示所有距离不超过k的点对上数的亦或值的和。
【样例输入】
6 3
7 4 4 3 2 0
2 1
6 2
5 6
3 5
4 1
【样例输出】
51
T1
题意概括:每个元素包含坐标 ,地盘 ,收益 , 只能由一种元素收益
首先DP方程
表示
选
表示
不选
要从所有
过继过来
要从之前所有的最大值过继过来
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
struct node
{
int pos,id;
long long data;
node(int x,long long y):pos(x),data(y){}
node(){}
bool operator <(node x) const
{
return pos>x.pos;
}
};
int n,a[100005],x[100005],h,t;
long long f[100005][2],ans,p[100005];
node q[100005];
priority_queue<node> o;
int main()
{
cin>>n;
for (int i=1;i<=n;i++) scanf("%d %lld %d",&a[i],&p[i],&x[i]);
long long y=0;
h=1;
for (int i=1;i<=n;i++)
{
while (h<=t)
{
if (a[i]>q[h].pos)
{
y=max(y,q[h].data+p[q[h].id]);
h++;
}
else break;
}
while (!o.empty())
{
if (a[i]>o.top().pos)
{
y=max(y,o.top().data);
o.pop();
}
else break;
}
f[i][0]=max(q[h].data,y);
f[i][1]=f[i][0]+p[i];
while (a[i]+x[i]<q[t].pos)
{
if (f[q[t].id][1]>y) o.push(node(q[t].pos,f[q[t].id][1]));
t--;
}
t++;
q[t].pos=a[i]+x[i];
q[t].data=f[i][0];
q[t].id=i;
ans=max(ans,f[i][1]);
}
ans=max(ans,f[n][0]);
cout<<ans;
}
T2
题意概括:一张图,使得 联通最少需要几条边
额
遍最短路,四个点两两最短路,要么路径不相交,相交则去重
#include<bits/stdc++.h>
using namespace std;
int n,m,u,v,b1[3010],b2[3010],b3[3010],b4[3010],d[3010],a,b,x,y,vi[3010];
int hed[3010],nex[6010],vt[6010],cnt,ans;
queue<int> id;
void add(int i,int j){
vt[++cnt]=j,nex[cnt]=hed[i],hed[i]=cnt;
vt[++cnt]=i,nex[cnt]=hed[j],hed[j]=cnt;
}
void bfs(int s,int *dd){
memset(dd,0x3f,sizeof(dd));
memset(vi,0,sizeof(vi));
id.push(s),dd[s]=0,vi[s]=1;
while(!id.empty()){
u=id.front(),id.pop();
for(int i=hed[u];i;i=nex[i]){
if(vi[vt[i]]) continue;
dd[vt[i]]=dd[u]+1,id.push(vt[i]),vi[vt[i]]=1;
}
}
}
int main(){
// freopen("graph.in","r",stdin);
// freopen("graph.out","w",stdout);
scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&x,&y);
for(int i=1;i<=m;i++) scanf("%d%d",&u,&v),add(u,v);
bfs(a,b1),bfs(b,b2),bfs(x,b3),bfs(y,b4);
ans=b1[b]+b3[y];
for(int i=1;i<=n;i++){
bfs(i,d);
for(int j=i;j<=n;j++){
int tmp=min(b1[i]+b3[i]+b2[j]+b4[j]+d[j],b2[i]+b4[i]+b1[j]+b3[j]+d[j]);
tmp=min(tmp,min(b1[i]+b4[i]+b2[j]+b3[j]+d[j],b1[j]+b4[j]+b2[i]+b3[i]+d[j]));
ans=min(ans,tmp);
}
}
printf("%d",ans);
}
T3
题意概括:RT
暴力的做法我就不献丑了
把题解拷上来
优化0:
首先每一个二进制位可以拆开来看,对答案没有影响。考虑一个树形DP,对每个二进制位分别统计对答案的影响。记f[x][i][j][0/1]为当前以x为根的子树中,深度为i的所有节点的第j位是0/1的数量。每将一个儿子y合并上去时,先利用前缀和统计两点分别在当前以x为根的子树和以y为根的子树中的答案,再将y的信息合并进x中。时间复杂度O(nklogA),期望得分40。
可以看出这个DP还可以继续优化。
优化1:使用点分治进行优化。每次合并时,用线段树统计答案,时间复杂度O(nlog2nlogA)。期望得分45-60。
优化2:继续使用点分治,观察到我们每次统计答案时都只会用线段树求一段前缀和,考虑改为使用前缀和,但发现这样合并一棵子树复杂度就不再是O(要合并的子树大小),而是O(整棵子树大小),会使复杂度爆炸,于是改为统计后缀和,这样总复杂度就降到了O(nlognlogA)。期望得分75-100。
优化3:观察原来的DP,发现第二维状态大小只和当前子树深度有关,于是使用长链剖分,依然沿用优化2中统计后缀和的方法合并子树信息和统计答案,时间复杂度降到了O(nlogA)。期望得分100。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read(){
int f=1,g=0;char ch=getchar();
for (;!isdigit(ch);ch=getchar()) if (ch=='-') f=-1;
for (;isdigit(ch);ch=getchar()) g=g*10+ch-'0';
return f*g;
}
const int N=500005;
int size,fir[N],n,k,depth[N],hs[N],cnt;
struct node{int f[20][2];}b[N],a[N],ta,tb;
node operator+(node a,int b){
for (int i=0;i<20;i++)
{a.f[i][b&1]++;b>>=1;}
return a;
}
void operator+=(node &a,node b){
for (int i=0;i<20;i++)
{a.f[i][0]+=b.f[i][0];a.f[i][1]+=b.f[i][1];}
}
node operator+(node a,node b){a+=b;return a;}
node operator-(node a,node b){
for (int i=0;i<20;i++)
{a.f[i][0]-=b.f[i][0];a.f[i][1]-=b.f[i][1];}
return a;
}
ll operator*(node &a,node &b){
ll ans=0;
for (int i=0;i<20;i++)
ans+=((ll)a.f[i][0]*b.f[i][1]+(ll)a.f[i][1]*b.f[i][0])<<i;
return ans;
}
node *f[N];
long long ans;
struct edge{int u,v,nex;}e[N*2];
void add(int u,int v){e[++size]=(edge){u,v,fir[u]};fir[u]=size;}
void clear(){
memset(fir,0,sizeof(fir));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(depth,0,sizeof(depth));
memset(hs,0,sizeof(hs));
}
void build(int x,int fa){
depth[x]=1;
for (int i=fir[x];i;i=e[i].nex)
if (e[i].v!=fa){
int y=e[i].v;build(y,x);
if (depth[y]+1>depth[x]){
depth[x]=depth[y]+1;
hs[x]=y;
}
}
}
void cre(int x){f[x]=b+cnt;cnt+=depth[x];}
node calc(int x,int i){
if (i<0) return f[x][0];
return (depth[x]<=i) ? b[0] : f[x][i];
}
node& calc(int x,int l,int r,node &a){return a=calc(x,l)-calc(x,r+1);}
void merge(int x,int y){
for (int i=0;i<depth[y];i++)
ans+=calc(y,i,i,ta)*calc(x,0,k-i-1,tb);
for (int i=0;i<depth[y];i++)
f[x][i+1]+=f[y][i];
f[x][0]+=f[y][0];
}
void dp(int x,int fa){
if (hs[x]){
f[hs[x]]=f[x]+1;
dp(hs[x],x);
f[x][0]=f[x][1]+a[x];
ans+=a[x]*calc(x,0,k,ta);
}
else f[x][0]=a[x];
for (int i=fir[x];i;i=e[i].nex)
if ((e[i].v!=fa)&&(e[i].v!=hs[x])){
int y=e[i].v;cre(y);dp(y,x);
merge(x,y);
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();k=read();
size=ans=0;cnt=1;
//clear();
for (int i=1;i<=n;i++){
int x=read();
a[i]=a[i]+x;
}
for (int i=1;i<n;i++){
int u=read(),v=read();
add(u,v);add(v,u);
}
build(1,0);cre(1);
dp(1,0);
printf("%lld\n",ans);
return 0;
}