luogu題目選做2
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;
}