A题:
作者成功地把所有的错误做法都试了一遍。
因为数只能往后移,所以在当前数后面有比它小的数的时候,这个数肯定要往后移,不然的话就没有机会往后移了。
所以,我们只要维护一个后缀最小值即可。
代码如下:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param n int整型
* @param a int整型vector
* @return int整型
*/
int wwork(int n, vector<int>& a) {
int s=a.back(),ans=0;
for(int i=n-2;i>=0;i--){
if(a[i]>s)ans++;
else s=a[i];
}
return ans;
}
};时间复杂度 O(n)
B题:
把表打出来后,通过手算或用OEIS找到规律即可。
规律就是 f[i]=i+f[i/2]*2 (i/2向下取整)
代码如下:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param n int整型
* @return long长整型
*/
long long calc(int x){
if(x==1)return 1;
return x+calc(x/2)*2;
}
long long Sum(int n){
return calc(n);
}
};时间复杂度 O(log n)
C题:
逐位处理。因为 ,所以可以枚举位数k从0到20进行处理。
我们定义,当两个点之间的路径第k位都为1时,就称这两个点之间可以抵达。
如果两条边相连点x,y,若p[x]和p[y]在二进制表示下的第k位都为1,那么说明x可以抵达的任何点都可以可以抵达y可以抵达的任何点,所以用并查集维护集合。
然后求出每个集合的大小,用siz来表示。
最后对于同一个集合内的任何点,他们之间都可以相互抵达,进行计算即可。注意要特殊处理路径为单个点的情况
代码如下:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param n int整型 点的个数
* @param u int整型vector 每条边的起点
* @param v int整型vector 每条边的终点
* @param p int整型vector 每个点的价值
* @return long长整型
*/
long long fa[100010],siz[100010],v[100010];
int get(int x){
if(x==fa[x])return x;
return fa[x]=get(fa[x]);
}
long long solve(int n, vector<int>& u, vector<int>& v, vector<int>& p) {
long long ans=0,s=1;
for(int k=0;k<=20;k++){
for(int i=0;i<n;i++){
fa[i]=i;
siz[i]=0;
}
for(int i=0;i<n-1;i++)
if(p[u[i]]>>k&1&&p[v[i]]>>k&1){
int x=get(u[i]),y=get(v[i]);
if(x==y)continue;
fa[x]=y;
}
for(int i=0;i<n;i++)
siz[get(i)]++;
for(int i=0;i<n;i++)
if(fa[i]==i&&p[i]>>k&1)ans=ans+(siz[i]+1)*siz[i]/2*s;
s=s*2;
}
return ans;
}
};时间复杂度 O(n log n)

京公网安备 11010502036488号