luogu题目选做(二)
P7885
从A点向B点行走的最有情况肯定是向着两个方向走,而不是四个方向。
因为,不能向一个方向连续走两次,所以最优情况是x和y交替走,必然可以走到只剩x或只剩y的情况,此时的最优解就是走长城的情况,然后假设我们需要走n步,可以再纸上模拟若n是奇数则走\(2\times n-1\)步若为偶数则走\(2\times n\)步
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int t;
long long a , b , c , d , x , y , ans , cnt ;
int main()
{
cin >> t;
while( t -- )
{
cin >> a >> b >> c >> d ;
x = abs( a - c ) , y = abs( b - d );
ans = x + y , cnt = abs( x - y );
if( cnt > 1 ) ans += cnt - cnt % 2;
cout << ans << endl;
}
return 0;
}
P7886
首先判断时候可以成立,若\(n\times m\)是\(k\)的倍数则一定可以,反之不行。
然后考虑构造答案。我想到的方法是走S形,因为要从后向前填颜色,可以离线涂色,最后一起输出
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n , m , k , t , color , cnt , a[N];
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> t;
while( t -- )
{
cin >> n >> m >> k;
if( n * m % k ) cout << "NO\n";
else
{
cout << "YES\n";
color = 1 , cnt = 0;
for( int i = 1 ; i <= n ; i ++ )
{
if( i % 2 )
{
for( int j = 1 ; j <= m ; j ++ )
{
cout << color << " ";
cnt ++;
if( cnt == k ) color ++ , cnt = 0;
}
cout << '\n';
}
else
{
for( int j = m ; j >= 1 ; j -- )
{
a[j] = color;
cnt ++;
if( cnt == k ) color ++ , cnt = 0;
}
for( int j = 1; j <= m ; j ++ ) cout << a[j] << ' ';
cout << '\n';
}
}
}
}
}
P7893
举一个例子15 3
答案是11,然后我们将1到15进行分组
1 3 9
2 6
4 12
5 15
7
8
9
10
11
13
14
很显然对于每一行来说我们的最有解就是隔一个选一个
对于第1列我们发现这些数都不是\(3^1\)的倍数,同理第二列都不是\(3^2\)的倍数,即第\(i\)列都不是\(p^i\)的倍数
对于第一列是数的总数是( n / p ) * ( p - 1 ) + ( x % p )
,然后我们n /= p * p
再算一遍就是
第三列,所以循环操作直到n==0
结束循环
#include <bits/stdc++.h>
#define f( x ) ( ( x / p ) * ( p - 1 ) + ( x % p ) )
using namespace std;
long long t , n , ans , p ;
inline long long read()
{
register long long x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
int main()
{
t = read();
while( t -- )
{
n = read() , p = read() , ans = 0;
if( p == 1 )
{
puts("0");
continue;
}
for( ; n ; n /= p * p ) ans += f(n);
printf( "%lld\n" , ans );
}
return 0;
}
P7892
数据范围太小直接枚举边长,计算栅栏长度
#include <bits/stdc++.h>
using namespace std;
int t , n , m , f ;
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
int main()
{
t = read();
while( t -- )
{
n = read() , m = read() , f = 0 ;
for( register int i = 1 ; i * i <= n && !f ; i ++ )
{
if( n % i ) continue;
if( ( i + n/i + 2 ) * 2 <= m ) f = 1 , puts("Good");
}
if( !f ) puts("Miss");
}
return 0;
}
P1197
离线做,倒着枚举被攻击点,将删点变成加点,加点的同时加所有与该点相连且另一点也存在的边,如果加边前两个点不在同一个连通块中,连通块总数减一
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n , k , m , fa[N] , attack[N] , ans[N] ;
bool vis[N];
vector < int > e[N];
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' )
{
x = ( x << 3 ) + ( x << 1 ) + ch - '0';
ch = getchar();
}
return x;
}
inline void addedge( const int &x , const int &y ) { e[x].push_back( y ) , e[y].push_back( x ); return; }
inline int getfa( int x ) { return fa[x] = ( ( fa[x] == x ) ? x : getfa( fa[x] ) ); }
inline void merage( int x , int y ) { register int fx = getfa( x ) , fy = getfa( y ) ; fa[ fx ] = fy ; return; }
int main()
{
n = read() , m = read();
for( register int i = 1 ; i <= n ; i ++ ) fa[i] = i;
for( register int i = 1 , x , y ; i <= m ; i ++ ) x = read()+ 1 , y = read() + 1 , addedge( x , y );
k = read();
for( register int i = 1 ; i <= k ; i ++ ) attack[i] = read() + 1 , vis[ attack[i] ] = 1;
for( register int i = 1 ; i <= n ; i ++ )
{
if( vis[i] ) continue;
for( auto it : e[i] )
{
if( vis[ it ] ) continue;
merage( it , i );
}
}
for( register int i = 1 ; i <= n ; i ++ ) if( !vis[i] && fa[i] == i ) ans[k] ++;
for( register int i = k , fx , fy ; i >= 1 ; i -- )
{
ans[ i - 1 ] = ans[i] + 1 , vis[ attack[i] ] = 0 , fx = getfa( attack[i] );
for( auto it : e[ attack[i] ] )
{
if( vis[it] ) continue;
fy = getfa( it );
if( fx == fy ) continue;
fa[fy] = fx , ans[ i - 1 ] --;
}
}
for( register int i = 0 ; i <= k ; i ++ ) printf( "%d\n" , ans[i] );
return 0;
}
P3397
差分
将每一行单独来做差分,最后对每一行求前缀和并输出
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int n , m , a[N][N];
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
int main()
{
n = read() , m = read();
for( register int i = 1 , x_1 , x_2 , y_1 , y_2 ; i <= m ; i ++ )
{
x_1 = read() , y_1 = read() , x_2 = read() , y_2 = read();
for( register int j = x_1 ; j <= x_2 ; j ++ ) a[j][ y_1 ] ++ , a[j][ y_2 + 1 ] --;
}
for( register int i = 1 ; i <= n ; i ++ )
{
for( register int j = 1 ; j <= n ; j ++ ) printf( "%d " , a[i][j] += a[i][ j - 1 ] );
printf("\n");
}
return 0;
}
二维差分
求二维差分的方法是
b[i][j] = a[i][j] - a[ i - 1 ][j] - a[i][ j - 1 ] + a[ i - 1 ][ j - 1 ]
对( x1 , y1 )
到( x2 , y2 )
进行区间修改的方法为
b[x1][y1] ++ , b[x1][ y2 + 1 ] -- , b[ x2 + 1 ][y1] -- , b[ x2 + 1 ][ y2 + 1 ] ++
还原的方法为
a[i][j] = b[i][j] + b[ i - 1 ][j] + b[i][ j - 1 ] - b[ i - 1 ][ j - 1 ]
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int n , m , a[N][N];
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
int main()
{
n = read() , m = read();
for( register int i = 1 , x_1 , x_2 , y_1 , y_2 ; i <= m ; i ++ )
{
x_1 = read() , y_1 = read() , x_2 = read() , y_2 = read();
a[x_1][y_1] ++ , a[ x_2 + 1 ][ y_1 ] -- , a[ x_1 ][ y_2 + 1 ] -- , a[ x_2 + 1 ][ y_2 + 1 ] ++;
}
for( register int i = 1 ; i <= n ; i ++ )
{
for( register int j = 1 ; j <= n ; j ++ ) printf( "%d " , a[i][j] += a[ i - 1 ] [j] + a[i][ j - 1 ] - a[ i - 1 ][ j - 1 ] );
puts("");
}
return 0;
}
数据水,两种做法差别不大
CF18C
做一遍前缀和,枚举第一组数的右端点
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n , a[N] , cnt = 0;
inline int read()
{
register int x = 0 , f = 1;
register char ch = getchar();
while( ( ch < '0' || ch > '9') && ch != '-' ) ch = getchar();
if( ch == '-' ) f = -1 , ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x * f;
}
int main()
{
n = read();
for( register int i = 1 ; i <= n ; i ++ ) a[i] = a[ i - 1 ] + read();
for( register int i = 1 ; i < n ; i ++ ) if( a[i] << 1 == a[n] ) cnt ++;
cout << cnt << endl;
return 0;
}
CF17B
用类似Kruskal的方法,把边进行排序,然后枚举每条边,判断v是否已经有上司,如果没有就加边
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 +5 , M = 1e4 +5;
int n , m , val[N] , cnt = 0 , father[N] , tt = 0;
long long ans;
struct edge
{
int u , v , w ;
friend bool operator < ( edge a , edge b ) { return a.w < b.w;}
} e[M];
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x ;
}
int main()
{
n = read();
for( register int i = 1 ; i <= n ; i ++ ) val[i] = read();
m = read();
for( register int i = 1 , u , v , w ; i <= m ; i ++ )
{
u = read() , v = read() , w = read();
if( val[u] < val[v] ) continue;
cnt ++ , e[cnt].u = u , e[cnt].v = v , e[cnt].w = w;
}
sort( e + 1 , e + 1 + cnt);
for( register int i = 1 ; i <= cnt ; i ++ )
{
if( father[ e[i].v ] ) continue;
father[ e[i].v ] = 1, ans += e[i].w , tt ++ ;
if( tt == n - 1 ) continue;
}
if( tt == n - 1 ) cout << ans << endl;
else puts("-1");
return 0;
}
AT795
贪心做,首先要选择最大的k个数,其实越早选择除2的次数越多,所以越大的数越后选择
#include <bits/stdc++.h>
using namespace std;
int n , k;
double ans;
vector< int > a;
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = x * 10 + ch - '0' , ch = getchar();
return x;
}
int main()
{
n = read() , k = read();
for( register int i = 1 ; i <= n ; i ++ ) a.push_back( read() );
sort( a.begin() , a.end() );
for( register int i = n - k ; i < n ;i ++ ) ans = ( ans + a[i] ) / 2.0;
printf( "%.6lf\n" , ans );
return 0;
}
P1249
暴力的枚举枚举每一个起点,然后用DFS遍历从该点开始的最长子链。
#include<bits/stdc++.h>
#define to first
#define val second
using namespace std;
const int N = 25;
int n , m , max_len = -1;
bool v[N];
vector< pair< int , int > > e[N];
void dfs( int x , int sum )
{
if( v[x] ) return ;
// cout << x << ' ' << sum << endl;
max_len = max( max_len , sum ) , v[x] = 1;
for( auto it : e[x] ) dfs( it.to , it.val + sum );
v[x] = 0;
}
int main()
{
cin >> n >> m;
for( int i = 1 , x , y , z ; i <= m ; i ++ )
{
cin >> x >> y >> z;
e[x].push_back( { y , z } ) , e[y].push_back( { x , z } );
}
for( int i = 1 ; i <= n ; i ++ )
{
dfs( i , 0 );
memset( v , 0 , sizeof( v ) );
}
cout << max_len << endl;
return 0;
}
P7001
数据范围小,O(n)
暴力匹配即可,遇到星号跳过即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n , cnt = 0;
string s , x , ans[N];
int main()
{
cin >> s >> n ;
for( register int i = 1 , f ; i <= n ; i ++ )
{
cin >> x;
f = 1;
for( register int j = 0 ; j < s.size() && f ; j ++ )
{
if( s[j] == '*' ) continue;
if( s[j] != x[j] ) f = 0;
}
if( f ) cnt ++ , ans[cnt] = x;
}
cout << cnt << endl;
for( register int i = 1 ; i <= cnt ; i ++ ) cout << ans[i] << endl;
return 0;
}