目录
D. Apple Tree
问题建模:
给定一棵有n个顶点的树,该树可以在任意位置放置一个苹果,每次摇动该树,苹果将向其所在节点的任意一个子节点移动,若当前节点为叶子节点,则该苹果将掉落。问给出q对放置苹果的位置,则最终这对苹果掉落位置构成的有序对数量可能为多少。
问题分析:
1.问题所求
问题所求有序对数量,等价于第一个苹果掉落位置数量*第二个苹果掉落位置数量
2.求解苹果掉落位置数量
苹果从一个位置出发,每次向任意根节点移动,最终移动到某一个叶子结点,则任意苹果的掉落数量,为以其初始位置所在子树的叶子节点数量。
3.依据所求,使用算法
由于有q对苹果有序对数量需要求解,则先采用记忆化搜索算法,从根节点开始,计算每一个节点所在子树的叶子结点数量并将其存储起来,方便后面q次查询进行直接的计算,时间复杂度为O(q+n)。
4.代码
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int> PII;
const int N=2e5+10;
int dp[N];
vector<int> e[N];
int dfs(int u,int c){
dp[u]=0;
for(auto v:e[u] ) {
if(v!=c) dp[u]+=dfs(v,u);
}
return dp[u]=max(dp[u],1);
}
void solve(){
int n;
cin >>n;
for(int i=0;i<n-1;i++){
int u,v;
scanf("%d %d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,1);
int q;
cin >>q;
while(q--){
int x,y;
scanf("%d %d",&x,&y);
printf("%lld\n",(LL)dp[x]*dp[y]);
}
for(int i=1;i<=n;i++) e[i].clear();
}
int main(){
int t=1;
cin >>t;
while(t--) solve();
return 0;
}
E. Tracking Segments
问题建模:
给定m个子区间,以及一个长度为n,初始元素都为0的序列。进行q次修改,每次修改选择序列的一个位置,并将其元素修改为1,问最早一次修改时存在一个子区间内的1的个数大于0的个数的修改编号为多少,若不存在,则输出-1。
1<=m<=n<=1e5
1<=l<=r<=n
1<=1<=n
问题分析:
1.分析修改操作与所求的关系
每一次修改,将序列的某一个位置变为1,然后判断m个区间内存不存在一个区间符合要求,可以考虑用前缀和的方法判断,即将区间求和,得到该区间1的个数,然后和该区间内的0的个数(区间大小-区间前缀和)进行比较。
由于每一次查询后序列都会修改一次,若采用前缀和的方法则在每一次查询后需要花费O(n)的时间来建立前缀和数组,然后遍历m个子区间进行查询判断,每次查询的时间为O(1),则总判断时间为O(n+m)。
2.分析修改变化趋势
由于对于每一次查询,序列内1的个数会逐渐变多,则当某一次查询使得某一个区间满足要求,则在此次查询之后的所有查询都满足要求。
3.结合变化,使用算法
由于满足条件的情况在某一次查询之后将会稳定维持,则可以通过二分的方法找到一个查询编号mid,对于该所选的编号,将初始序列改变为编号1~编号mid所做操作后的序列,然后建立前缀和数组,然后对m个子区间进行判断。根据编号是否满足条件来考虑是将编号缩小还是放大,从而来找到最小满足条件的编号,其复杂度为(2*n+m)*log(q)。
4.代码
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int> PII;
const int N=1e5+10,INF=1e9;
int q[N];
LL b[N];
PII sub[N];
int n,m,p;
bool check(int k){
memset(b,0,sizeof(LL)*(n+1));
for(int i=1;i<=k;i++){
b[q[i]]=1;
}
for(int i=1;i<=n;i++){
b[i]+=b[i-1];
}
for(int i=1;i<=m;i++){
int one=b[sub[i].y]-b[sub[i].x-1];
if(one>sub[i].y-sub[i].x+1-one){
return true;
}
}
return false;
}
void solve(){
cin >>n >>m;
for(int i=1;i<=m;i++){
scanf("%d %d",&sub[i].x,&sub[i].y);
}
cin >>p;
for(int i=1;i<=p;i++) scanf("%d",&q[i]);
if(check(p)){
int l=1,r=p;
while(l<r){
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout <<l <<"\n";
}else puts("-1");
}
int main(){
int t=1;
cin >>t;
while(t--) solve();
return 0;
}