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;
}
posted @ 2021-10-15 21:00  PHarr  阅读(47)  评论(0编辑  收藏  举报