A题 A. AvtoBus
题意分析
有n个轮子,分别是4轮车和6轮车上的,问最少和最多有多少辆车?如果不合法,输出-1
思路分析
奇数的情况肯定是不合法的,输出-1. 然后,我们需要知道
4
x
+
6
y
=
n
4x+6y=n
4 x + 6 y = n ,我们需要想办法最大化和最小化x+y。分情况讨论。
最小化的时候,肯定是优先满足六轮车,
n
u
m
1
=
n
6
num1=\frac{n}{6}
n u m 1 = 6 n ,剩下
m
=
n
−
6
n
u
m
1
m=n-6num1
m = n − 6 n u m 1 ,那么此时四轮车
m
4
\frac{m}{4}
4 m 辆,为如果m除以4此时有余数,肯定是2,那么我们只需要把6辆的车减少一辆,和这个2合并成两辆四轮车。 最大化的时候,肯定是优先满足四轮车,
n
u
m
1
=
n
4
num1=\frac{n}{4}
n u m 1 = 4 n ,剩下的就是
m
=
n
−
4
∗
n
u
m
1
m=n-4*num1
m = n − 4 ∗ n u m 1 ,如果此时有m除以6有余数,肯定是2,所以我们从四轮车里面拿出一辆和2拼接成一个六轮车就行了。
代码
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve ( ) {
ll n;
cin>> n;
if ( n& 1 || n<= 2 ) {
puts ( "-1" ) ;
} else {
ll ans1= n/ 6 ;
ll now= n- 6 * ans1;
if ( now== 2 ) {
ans1-- ;
now+= 6 ;
}
ans1+= now/ 4 ;
ll ans2= n/ 4 ;
now= n- 4 * ans2;
if ( now== 2 ) {
ans2-- ;
now+= 4 ;
}
ans2+= now/ 6 ;
cout<< ans1<< " " << ans2<< endl;
}
}
int main ( ) {
int t;
cin>> t;
while ( t-- ) {
solve ( ) ;
}
return 0 ;
}
B题 Stone Age Problem
题意分析
给出一个数组,两种操作。
替换第
i
i
i 个位置的数为
x
x
x 将数组的所有数都替换成
x
x
x 我们需要计算每次操作之后的数组的和,注意,操作之间是相互干扰的,也就是后面一次的操作都是在前面一次的操作的基础上实现的。
思路分析
我们给每个位置维护一个
v
e
r
s
i
o
n
version
v e r s i o n 版本号,同时用一个
n
v
nv
n v 来维护全局修改的版本号。
如果是第一种操作,先比较这个位置的版本号是否是小于最新的全局修改的版本号,如果小于,那么就将这个位置的版本号和当前最新的全局修改版本号
n
v
nv
n v 同步,注意需要更新这个位置的数字。如果大于等于,那么执行正常的替换操作。 如果是第二种操作,那么我们直接更新全局修改的版本号即可,注意此时没有必要更新每个位置的值。因为全局更新带来的问题我们可以使用一个
n
v
nv
n v 版本号和
n
o
w
now
n o w 来维护。
代码
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[ 200010 ] ;
int tag[ 200010 ] ;
void solve ( ) {
memset ( tag, 0 , sizeof ( tag) ) ;
int n, q;
scanf ( "%d %d" , & n, & q) ;
ll sum= 0 ;
for ( int i= 1 ; i<= n; i++ ) {
scanf ( "%d" , & a[ i] ) ;
sum+= a[ i] ;
tag[ i] = 0 ;
}
int nv= 0 ;
ll now;
while ( q-- ) {
int t;
scanf ( "%d" , & t) ;
if ( t== 1 ) {
int i, x;
scanf ( "%d %d" , & i, & x) ;
if ( tag[ i] < nv) {
tag[ i] = nv;
sum-= now; sum+= x;
a[ i] = x;
} else {
sum-= a[ i] ; sum+= x; a[ i] = x;
}
} else {
ll x;
scanf ( "%lld" , & x) ;
now= x;
nv++ ;
sum= x* n;
}
printf ( "%lld\n" , sum) ;
}
}
int main ( ) {
int t;
t= 1 ;
while ( t-- ) {
solve ( ) ;
}
return 0 ;
}
C题 C. Rooks Defenders
题意分析
给出一个棋盘,可以在某一个位置放置一个车,同时可以拿走某一个位置的车(保证这个位置一定有车)。多次询问某一个矩阵的所有的位置是否可以被至少一个车到达。如果有,输出YES
,否则输出NO
注意这里的车是可以横向水平移动和纵向垂直移动的。
思路分析
我们观察到,如果想要一个矩阵的每个位置都必须被至少一个车到达,有两种情况,一种是这个矩阵的所有的行都至少有一个车,或者是这个矩阵的所有的列都至少有一个车。两个只需要满足一种情况,这个矩阵就是可完全覆盖的。 所以,行和列分开讨论。对于行或者是列,我们需要维护一个区间的区间和,这个区间和就是这个区间内有多个车,表示的是有多少行或者是多少列被覆盖了。对于放置车或者是拿走车的操作,其实就是类似于单点修改,不过我们要从行和列两个维护同时维护就是了。 问题抽象出来最后就是维护两个树状数组,分别是行和列,然后就是一个很简单的单点修改和区间查询了。
代码
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 100010 ;
int hang[ N] , lie[ N] ;
int c1[ N] , c2[ N] ;
void add1 ( int x, int k) {
for ( ; x< N; x+= x& ( - x) ) {
c1[ x] += k;
}
}
int query1 ( int x) {
int sum= 0 ;
for ( ; x; x-= x& ( - x) ) {
sum+= c1[ x] ;
}
return sum;
}
void add2 ( int x, int k) {
for ( ; x< N; x+= x& ( - x) ) {
c2[ x] += k;
}
}
int query2 ( int x) {
int sum= 0 ;
for ( ; x; x-= x& ( - x) ) {
sum+= c2[ x] ;
}
return sum;
}
void solve ( ) {
memset ( hang, 0 , sizeof ( hang) ) ;
memset ( lie, 0 , sizeof ( lie) ) ;
int n, q;
scanf ( "%d %d" , & n, & q) ;
while ( q-- ) {
int t;
int x, y;
int a, b, c, d;
scanf ( "%d" , & t) ;
if ( t== 1 ) {
scanf ( "%d %d" , & x, & y) ;
if ( hang[ x] == 0 ) {
add1 ( x, 1 ) ;
}
hang[ x] ++ ;
if ( lie[ y] == 0 ) {
add2 ( y, 1 ) ;
}
lie[ y] ++ ;
} else if ( t== 2 ) {
scanf ( "%d %d" , & x, & y) ;
hang[ x] -- , lie[ y] -- ;
if ( hang[ x] == 0 ) {
add1 ( x, - 1 ) ;
}
if ( lie[ y] == 0 ) {
add2 ( y, - 1 ) ;
}
} else {
scanf ( "%d %d %d %d" , & a, & b, & c, & d) ;
int sum1= query1 ( c) - query1 ( a- 1 ) ;
int sum2= query2 ( d) - query2 ( b- 1 ) ;
if ( sum1== c- a+ 1 || sum2== d- b+ 1 ) {
puts ( "Yes" ) ;
} else {
puts ( "No" ) ;
}
}
}
}
int main ( ) {
int t;
t= 1 ;
while ( t-- ) {
solve ( ) ;
}
return 0 ;
}
D题 Toss a Coin to Your Graph…
题意分析
给出一个图,对于所有路径长度为k的路径,我们需要让路径的最大值最小化。没有的话输出-1.
思路分析
最大值最小化,一般都是二分的解法。我们发现,最后的答案有二分性。所以我们二分最后的答案,然后进行判断。 注意,这里我们判断的时候,如果这个结点的权值大于我们二分的值,显然是不符合要求的。所以我们需要过滤掉所有的权值大于二分的值的情况。然后对于剩下的图,如果有环,那么肯定是符合要求的,如果没有,我们可以维护过滤之后的最长路径,如果不小于
k
k
k ,那么也是符合要求的。
代码
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 200010 ;
vector< int > v[ N] ;
int a[ N] ;
int cnt[ N] ;
ll dp[ N] ;
int n, m;
ll k;
bool cycle;
ll dfs ( int x, int sum) {
if ( a[ x] > sum) return 0 ;
if ( cnt[ x] == 1 ) return k;
if ( cnt[ x] == 2 ) return dp[ x] ;
dp[ x] = 1 ; cnt[ x] = 1 ;
for ( auto y: v[ x] ) {
dp[ x] = max ( dp[ x] , dfs ( y, sum) + 1 ) ;
}
cnt[ x] = 2 ;
return dp[ x] ;
}
bool judge ( int x) {
memset ( cnt, 0 , sizeof ( cnt) ) ;
for ( int i= 1 ; i<= n; i++ ) {
if ( ! cnt[ i] ) {
if ( dfs ( i, x) >= k) {
return true ;
}
}
}
return false ;
}
int main ( ) {
cin>> n>> m>> k;
for ( int i= 1 ; i<= n; i++ ) {
cin>> a[ i] ;
}
for ( int i= 1 ; i<= m; i++ ) {
int x, y;
cin>> x>> y;
v[ x] . push_back ( y) ;
}
int ans= 1e9 + 7 ;
int l= 1 , r= 1e9 , mid;
while ( l<= r) {
mid= ( l+ r+ 1 ) >> 1 ;
if ( judge ( mid) ) {
ans= mid;
r= mid- 1 ;
} else {
l= mid+ 1 ;
}
}
if ( ans== 1e9 + 7 ) ans= - 1 ;
cout<< ans<< endl;
return 0 ;
}