地球發動機(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;
}