A
选正奇数 增加,正奇数减少,首先判断两个数的大小关系。看是该增加还是减少,然后答案显然只有0,1,2。
int main(){
int t = read();
while(t--){
ll a,b;
a = read();b = read();
if(a == b) puts("0");
else if(a < b){
if((b - a) % 2 != 0) puts("1");
else {
puts("2");
}
}
else{
if((a - b) % 2 == 0) puts("1");
else puts("2");
}
}
}
B
给你 n n n个数字,然后再给你 m m m个数, p 1 , p 2 . . . p m p_1,p_2...p_m p1,p2...pm,你可使任意次交换 p i , p i + 1 p_i,p_i+1 pi,pi+1处的 位置。问你是否可以排成非递减序列。
思路:
冒泡即可。只不过只能交换 p i , p i + 1 p_i,p_i+1 pi,pi+1位置的数。
O ( n ⋅ m ) O(n\cdot m) O(n⋅m)
int a[200];
int b[200];
int main(){
int t = read();
while(t--){
int n = read(),m = read();
for(int i = 1;i <= n;++i) a[i] = read();
for(int i = 1;i <= m;++i){
b[i] = read();
}
for(int i = 1;i <= n;++i){
for(int j = 1;j <= m;++j){
if(a[b[j]] > a[b[j]+1]) swap(a[b[j]],a[b[j]+1]);
}
}
if(is_sorted(a+1,a+n+1)) puts("YES");
else puts("NO");
}
}
O ( n ) O(n) O(n)做法
给定 p p p,可交换 a [ p ] a[p] a[p]和 a [ p + 1 ] a[p+1] a[p+1],其他的都不能发生交换,所以我们可以看成一个个小段,且当前小段的最小值要大于等于前一个小段的最大值才行,而在一个小段的内部,可能并不是有序的,所以我们只记录这个小段的最大值就行。
bool vis[200];
int a[200];
void solve(){
memset(vis,0,sizeof vis);
int n = read(),m = read();
rep(i,1,n) a[i] = read();
rep(i,1,m) {
int b = read();
vis[b] = 1;
}
bool ok = true;
int M = 0;
int ax = a[1];
rep(i,2,n){
if(!vis[i-1]) M = ax;//前一个不能交换,说明当前元素和前一个元素不是一个小段中的,取最大值
if(M > a[i]) {ok = 0;break;}
ax = max(ax,a[i]);
}
if(ok) puts("YES");
else puts("NO");
}
int main(){
int t = read();
while(t--){
solve();
}
}
C
给你一个一个字符串,里面的字母代表开关,你从头开始按,按到某一处出错后,下一次要从头开始按。给你 m个数,代表着按错的开关的位置。让你输出26个字母。表示开关被按下的次数。
思路:
开关在第 i i i个位置处出错,说明第 i i i个位置按下后,下一次要从1开始按,也就是 [ 1 , i ] [1,i] [1,i]位置的开关都多按了一次。我们不妨设有一个数组 d d d, d [ i ] d[i] d[i]意思是 i i i之前的位置多按了几次。然后遍历 m m m个错误个位置累加贡献,最后倒着累加,最后统计即可。
ll f[N];
char s[N];
ll b[28];
int main(){
int t = read();
while(t--){
memset(f,0,sizeof f);
memset(b,0,sizeof b);
int n = read(),m = read();
scanf("%s",s+1);
for(int i = 1;i <= m;++i){
ll a= read();
f[a] ++;
}
for(int i = n-1;i >= 1;i--) f[i] += f[i+1];
for(int i = 1;i <= n;++i){
b[s[i]-'a'] += f[i] + 1;
}
for(int i = 0;i < 26;++i) printf("%lld ",b[i]);
puts("");
}
}
D
题意给你三个数 A , B , C A,B,C A,B,C,然后每个数都可以+1 / -1。让你输出A | B && B | C 需要进行的最小操作数。
思路:
1、枚举倍数
复杂度为 n l o g n nlogn nlogn级別,并非 n 2 n^2 n2级别。。。还要注意上线设大些
int res[3];
int main(){
int t = read();
while(t--){
ll a = read(),b = read(),c = read();
ll ans = INF;
for(int i = 1;i <= 2e4;++i){
for(int j = i;j <= 2e4;j += i){
for(int k = j;k <= 2e4;k += j){
int d = abs(i-a)+abs(j-b)+abs(k-c);
if(ans > d) {ans = d;res[0] = i;res[1] = j;res[2] = k;}
}
}
}
cout << ans<<endl;
cout << res[0] <<' '<<res[1] <<' '<<res[2]<<endl;
}
}
2、预处理 2 e 4 2e4 2e4级别的因子,枚举 b b b,然后 O ( 1 ) O(1) O(1)得到c,然后a必为b的因子,枚举一下a。取最小。
int res[3];
vector<int> Q[N];
void init(){
for(int i = 1;i <= 2e4;++i){
for(int j = 1;j <= sqrt(i);++j){
if(i % j == 0) {Q[i].push_back(j);if(i/j != j) Q[i].push_back(i/j);}
}
}
}
int main(){
init();
int t = read();
while(t--){
ll a = read(),b = read(),c = read();
ll ans = INF;
for(int i = 1;i <= 2e4;++i){
int d = c / i;
int c1 = d * i,c2 = (d+1)*i;
if(abs(c1-c) > abs(c2-c)) swap(c1,c2);//c1为c
ll sum = abs(c1 - c) + abs(i-b);
int S = Q[i].size();
for(int j = 0;j < S;++j){
ll a1 = Q[i][j];//枚举a值
if(ans > sum + abs(a-a1)) ans = sum + abs(a-a1),res[0] = a1,res[1] = i,res[2] = c1;
}
}
cout << ans<<endl;
cout << res[0] <<' '<<res[1] <<' '<<res[2]<<endl;
}
}
E
题意:
你有 n n n个节点,然后让你构造一棵二叉树,使得各个节点的深度之和为 d d d,不能输出 N O NO NO,否则输出YES,并写出从 2 2 2到 n n n节点的父亲节点。
思路:
构造题。
首先, n n n个节点的二叉树,深度之和最大的情况是树链,最小的情况是完全二叉树的情况,如果 d d d处于这个范围内肯定有解。
然后如何构造,我们从树链的情况,然后一步步往上移动最后一个节点,没上调一层,总值减一,一直到无法上调(即上一层已满),这期间判断下是否为 d d d。
上述便是构造过程,我们可以设一个 c n t cnt cnt数组来记录每一层的节点个数,初始1-n层都有一个节点。经过构造后 c n t [ i ] cnt[i] cnt[i]便是第 i i i层的节点个数。最后我们在由cnt构造出二叉树,然后写出答案。
在我的构造方案里似乎,构造二叉树的过程更难一些。。。难受
构造二叉树过程不想写了。。。如果有疑问的话在说声,我在补上。
int cnt[5005];//记录每一层的节点个数
int ax[5005];//每一层节点最多有多少
int pre[5005];//储存答案
int temp[2][5005];//这个是为了构造二叉树用的
void solve(){
memset(cnt,0,sizeof cnt);
int n = read(),d = read();
int num = (n-1)*n/2;//树链
rep(i,1,n) cnt[i] ++;
if(d > num) {puts("NO");return ;}
ax[1] = 1;
rep(i,2,15) ax[i] = ax[i-1]*2;
int p = n;
bool ok = true;
while(true){//构造过程
if(num <= d) break;
int idx = p,tmp = num;
ok = true;//最下层节点能否上移
while(cnt[idx-1]!=ax[idx-1]&& tmp > d){
idx --; tmp --; ok = false;
}
if(ok) break;
cnt[idx]++;cnt[p--]--;num = tmp;
}
if(num != d) puts("NO");
else {
puts("YES");
ok = false;
temp[!ok][1] = 1;
int tot = 2;int inx = 1;
while(cnt[tot]){
int op = 0;p = 1;
for(int i = 1;i <= cnt[tot];i ++){//构造二叉树
temp[ok][i] = ++inx;
if(op == 0) pre[inx] = temp[!ok][p];
else pre[inx] = pre[inx-1],++p;
op = (++op)%2;
}
ok = !ok;tot ++;
}
rep(i,2,n) cout << pre[i]<<' ';
puts("");
}
}
int main(){
int t = read();
while(t--){
solve();
}
}
F
题意:
有 n n n个点在 X X X轴上,初始坐标值分别为 a 1 , . . . . a n a_1,....a_n a1,....an,然后每个点都有一个速度 v i v_i vi,定义 d ( i , j ) d(i,j) d(i,j)是经过 t t t秒后,两个点的最小距离。让你求 ∑ 1 < = i < j < = n n d ( i , j ) \sum\limits_{1<=i
思路:
我们考虑对答案的贡献。显然对于 x i , x j x_i,x_j xi,xj,不失一般性设 x i < x j x_i
树状数组 +离散化
我们按 a i a_i ai从小到大排序,对每个点,我们只需要查询比 a i a_i ai的点且小于等于 v i v_i vi的点,那么答案的贡献是比他小的点的个数 ⋅ a i − S u m \cdot a_i-Sum ⋅ai−Sum,其中 S u m Sum Sum为小于等于 v i v_i vi的 a i a_i ai的和值。
struct node{
int a,b;
}res[N];
bool cmp(node A,node B){
return A.a < B.a
}
int b[N];ll c[N][2];
int n;
void add(int x,int num){
for(;x <= n;x += x&(-x)) c[x][0] ++,c[x][1] += num;
}
ll ask(int x,int k){
ll ans = 0;
while(x > 0){
ans+= c[x][k];
x -= x&(-x);
}
return ans;
}
int main(){
n = read();
rep(i,1,n) res[i].a = read();
rep(i,1,n) b[i] = res[i].b = read();
sort(res+1,res+n+1,cmp);
sort(b+1,b+n+1);
int m = unique(b+1,b+n+1) - b - 1;
ll ans = 0;
rep(i,1,n){
int x = lower_bound(b + 1,b +m + 1,res[i].b) - b;
ans += res[i].a*ask(x,0) - ask(x,1);
add(x,res[i].a);
}
cout << ans;
}