2022寒假训练week5

Day1

AcWing 1913.公平摄影

首先我们考虑全是HG的情况其实很简单就是遍历就好

然后就是把H赋值为1G赋值为 -1,然后做前缀和若s[l]==s[r]则证明[l+1,r]中的H,G是一样多的,因为我们要找最长的我们只要保存s[i]每个值第一次出现的位置就行,剩下就是数据范围比较大需要离散化

#include <bits/stdc++.h>
#define F first
#define S second
using namespace std;

const int N = 1e5+5;
int n , sum , res;
unordered_map<int,int>pos;
pair<int,int> cow[N];

int main()
{
    cin >> n;
    pos[0] = 0;
    char x;
    for( int i = 1 ; i <= n ; i ++ )
    {
        cin >> cow[i].F >> x ;
        cow[i].S = ( x == 'G' ? -1 : 1 );
    }

    sort( cow + 1 , cow + 1 + n );
    int last = cow[1].F;
    for( int i = 1 ; i <= n ; i ++ )
    {
        sum += cow[i].S;

        if( pos.count(s[i]) )
            res = max( res , cow[i].F - cow[ pos[s[i]]+1 ].F );
        else pos[s[i]] = i;

        if( cow[i].S == cow[i-1].S )
            res = max( res , cow[i].F - last );
        else last = cow[i].F;
    }
    cout << res << endl;
}

AcWing1875. 贝茜的报复

记录每个字母数奇偶数的个数,因为只要考虑结果的奇偶性所以枚举每个数奇偶就行,奇数用1偶数用2,然后(B+I+S+S+E+E)*(G+O+E+S)*(M+O+O)的奇偶性和(B+I)*(G+O+E+S)*(M)相同可以简化一点运算,最后的复杂度是\(2^7\)

#include <bits/stdc++.h>
#define F first
#define ll long long
#define S second
using namespace std;

const int N = 1e5+5;
const string mp = "BESIGOM";
int n , cnt , sum ;
unordered_map<char , int > p;
unordered_map<char , pair<int,int> > h;

void dfs( int t , int r )
{
    if( t == 7 )
    {

        sum =  (p['B']+ p['I'] )*(p['G']+p['O']+p['E']+p['S'])*(p['M']);
        if( sum % 2 == 0 ) cnt += r ;
        return ;
    }
    p[ mp[t] ] = 1 , dfs( t + 1 , r * h[ mp[t] ].F );
    p[ mp[t] ] = 2 , dfs( t + 1 , r * h[ mp[t] ].S );
}

int main()
{
    for( int i = 0 ; i < 7 ; i ++ ) h[ mp[i] ].F = h[ mp[i] ].S = 0;

    cin >> n;
    char ch;
    for( int x ; n ; n -- )
    {
        cin >> ch >> x;
        if( x&1 ) h[ch].F++;
        else h[ch].S++;
    }
    dfs( 0 , 1);
    cout << cnt << endl;
}

Day2,3

22牛客寒假训练营4

A R

无论怎么取,我们都都不能取P,所以先把字符串按照P分隔。

对于一每一段字符串,将R转化为1其他的转化成0,然后做一个前缀和。然后我们枚举左端点,二分出符合条件最靠左的右端点,从这个端点向右的点都可以的

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5+5;
int b[N] , ed[N];
ll n , k , m , res;
string s;

int main()
{
    cin >> n >> k >> s;
    for( int i = 1 ; i <= n ; i ++ )
    {
        if( s[i-1] == 'P' ) b[i] = 0 , ed[i] = i;
        else b[i] = b[i-1] + ( s[i-1] == 'R' );
    }
    ed[n+1] = n+1;
    for( int i = n ; i >= 1 ; i -- )
    {
        if( ed[i] ) continue;
        ed[i] = ed[i+1];
    }

    for( int i = 1 , l , r , mid , ans ; i <= n ; i ++ )
    {
        if( b[ed[i]-1] - b[i-1] < k ) continue;
        l = i , r = ed[i]-1 , ans = -1;
        while( l <= r )
        {
            mid = ( l + r ) >> 1 ;
            if( b[mid] - b[i-1] >= k ) ans = mid , r = mid - 1;
            else l = mid + 1;
        }
        res += ed[i] - ans;
    }
    cout << res << endl;
}

C 蓝彗星

用两个查封数组来分别表示蓝红,然后前缀和并枚举每一个点就行

#include <bits/stdc++.h>
#define ll long long
#define F first
#define S second
using namespace std;

const int N = 1e5+5;
int n , t , a[2*N] , b[N*2] , m , res;
string s;

inline int read()
{
	int x = 0 , 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() , t = read();
	cin >> s;
	for( int i = 1 , x ; i <= n ; i ++ )
	{
		x = read();
		m = max( m , x );
		if( s[i-1] == 'B' ) a[x] ++ , a[x+t]--;
		else b[x] ++ , b[x+t] --;
	}
	for( int i = 1 ; i < m + t ; i ++ )
	{
		 a[i] += a[i-1] , b[i] += b[i-1];
		 if( a[i] > 0 && b[i] == 0 )  res ++;
	}
	cout << res << endl;
}

D 雪色光晕

对于每次给定两个点,用两点式得到直线方程,然后转发成点斜式。点到直线的最短距离式做垂线,所以通过斜率得到另一个直线的斜率,然后用斜率和点得到垂线的解出交点判断交点是否在线段中。

但是点斜式有特列就是斜率为零和斜率不存在,如果不在线段上就要算到两个端点的距离并取最小值。在比赛中没有考虑点在线段上这种情况导致没有过

import math
n = int(input())
a,b,x,y = map(float, input().split(' '))
res = math.sqrt( (x-a)**2 + (y-b)**2 )
for i in range(0 , n):
    xi ,yi = map( int , input().split(' ') )
    c = a + xi
    d = b + yi
    res = min( res , math.sqrt((x-c)**2 + (y-d)**2 ) )
    if yi == 0 :
        if (x>=min(a,c) and x<= max(a,c) ):
            res = min( res , abs(y-b))
    elif xi == 0 :
        if (y>=min(b,d) and y <= max(b,d)):
            res = min( res , abs(x-a))
    else:
        k1 = (b-d)/(a-c)
        b1 = b-k1*a
        k2 = -1.0/k1
        b2 = y-k2*x
        xd = (b1-b2)/(k2-k1)
        if(xd >= min(a,c) and xd <= max(a,c) ):
            yd = k1*xd+b1
            res = min( res , math.sqrt( (x-xd)**2 + (y-yd)**2 ) )
    a=c
    b=d
print(res)

E 真假签到题

把代码在本地一跑就找到规律了,直接输出就好

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ll x;
    cin >> x;
    cout << x << endl;
}

F小红的记谱法

纯纯大模拟,我比赛的时候没读完结果反着模拟了一边,测试样例的时候才发现写反了

#include<bits/stdc++.h>
using namespace std;

const int N = 1005;
int b[N] , tp , nw;
char a[N];
string s , e ="6712345";

int main()
{
	cin >> s;
	for( int i = 0 ; i < s.size() ; i ++ )
	{
		if( s[i] >= 'A' && s[i] <= 'G' ) a[++tp] = e[s[i] - 'A'] , b[tp] = nw;
		else if( s[i] == '<' ) nw --;
		else nw ++;
	}
	for( int i = 1 ; i <= tp ; i ++ )
	{
		cout << a[i];
		while( b[i] < 0 ) printf(".") , b[i] ++;
		while( b[i] > 0 ) printf("*") , b[i] --;
	}
	return 0;
}

H真真真真签到题

确实很签到,就是给半个体对角线

import math
x = int(input())
y = x * x * 4 / 3
y = math.sqrt(y)
y = y ** 3
print(y)

J 区间合数的最小公倍数

首先用线性筛筛出所有的素数,将剩下的合数每一个都质因数分解,并且所有保存下每一个素数的最高次幂。最后把所有素数的最高次幂加起来。

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 30005 , mod = 1000000007;
int l , r , m , po[N];
ll res = 1;
bool notprime[N] , f;
vector<int> prime;

ll power( ll x , ll y )
{
    ll ans = 1 % mod;
    for( ; y ; y >>= 1 )
    {
        if( y & 1 ) ans = ans * x % mod;
        x = x * x % mod;
    }
    return ans;
}

inline void primes( int n )
{
    for( int i = 2 ; i <= n ; i ++ )
    {
        if( !notprime[i] ) prime.push_back(i);
        for( auto it : prime )
        {
            if( it * i > n ) break;
            notprime[ i * it ] = 1;
            if( i % it == 0 ) break;
        }
    }
}


int main()
{
    cin >> l >> r;
    primes(r);
    m = prime.size();
    for( int i = l , t ; i <= r ; i ++ )
    {
        if( !notprime[i] ) continue;
        f = 1 , t = i;
        for( int j = 0 , cnt ; j < m ; j ++ )
        {
            if( t % prime[j] ) continue;
            cnt = 0;
            while( t % prime[j] == 0 ) cnt ++ , t /= prime[j];
            po[j] = max( po[j] , cnt );
        }
    }
    if(!f)
    {
        cout << "-1\n";
        return 0;
    }

    for( int i = 0 ; i < m ; i ++ )
        res = res * power( prime[i] , po[i] ) % mod;
    cout << res << endl;

}

Day4,5

22牛客寒假训练营5

D 数位小孩

首先我们找出0到9所有和为素数的组合,然后按照这个组合用dfs枚举就一定能满足条件一,然后dfs的过程中统计顺便有没有一就可以满足条件二,至于条件三,只要从高位开始枚举,就可以满足。不断枚举,不断判断是否在[l,r]中,只要在就计数器加一

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;


int ln , rn;
ll l , r , res ;
bool pri[] = {0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1};
vector<int> p[10];

void dfs( ll num , int la , bool flag)
{
    if( num > r ) return ;
    if( num >= l && flag ) res ++;
    ll cur = num * 10;
    if( cur >= r ) return ;
    for( auto it : p[ la ] ) dfs( cur + it , it , flag || (it==1) );
    return ;

}


int main()
{
    for( int i = 0 ; i <= 9 ; i ++ )
        for( int j = 0 ; j <= 9 ; j ++ )
            if( pri[i+j] ) p[i].push_back(j);

    cin >> l >> r;
    ln = log10(l)+1 , rn=log10(r)+1;
    for( int i = 1 ; i <= 9 ; i ++ )
        dfs( i , i , ( i == 1 ) );
    cout << res << endl;
    return 0;
}

G 163小孩

#include<bits/stdc++.h>
using namespace std;

int cnt;

void dfs( int x , int sum )
{
    if( x == 14 )
    {
        cnt += ( sum == 6 );
        return ;
    }
    for( int i = 0 ; i <= 4 ; i ++ )
        dfs( x + 1 , sum + i );
    return ;
}

int main()
{
    dfs( 1 , 0 );
    cout << cnt << endl;
    return 0;
}

虽然这个程序会TLE但是,可以直接在本地跑出来然后输出

print("18395")

然后我们可以也可以用数学方法算出来

首先假设用字母代表不同数字,

abcdef
aabcde
aabbcd
aaabcd
aaabbc
aaabbb
aaaabc
aaaabb

所以答案就是\(C_{13}^6+C_{13}^5C_6^1+C_{13}^4C_4^1C_3^1+C_{13}^4C_4^1+C_{13}^3C_3^1C_2^1+C_{13}^2+C_{13}^3C_3^1+C_{13}^2C_2^1\)

这个具体做法可以参考下图24点的做法来理解

24点做法

I 兔崽小孩

按照区间进行分段,并把区间排序求前缀和

对于每一个询问二年分出最小大于k的区间,用前缀和快速求出所有的和判断即可

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 1e6+5;
int n , q , m , k , p;
int a[N] , b[N];

inline int read()
{
    int x = 0 , 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;
}

void slove()
{
    k = read() , p = read();
    int t = upper_bound( a + 1 , a+1+m , k ) - a;
    if( t > m )
    {
        if(p) printf("No\n");
        else printf("Yes\n");
        return ;
    }
    int cnt = b[m] - b[t-1] - (m-t+1)*k;
    if( cnt >= p ) printf("Yes\n");
    else printf("No\n");
}

int main()
{
    n = read() , q = read() , m = n-1;
    int t0 = read();
    for( int i = 2 , t1 ; i <= n ; i ++ )
        t1 = read() , a[i-1]= t1 - t0 , t0=t1;
    sort( a + 1 , a + 1 + m );
    for( int i = 1 ; i <= m ; i ++ )
        b[i] = a[i] + b[i-1];
    while(q--) slove();
    return 0;
}

J 三国小孩

我的牌多一定赢

n , m , k = map(int , input().split(' '))
if n + m > k:
    print("YES")
else :
    print("NO")

Day6,7

22牛客寒假训练营6

D 删除子序列

贪心的删掉最靠前的即可

#include <bits/stdc++.h>
#define ll long long
#define F first
#define S second
#define T (t[i]-'a')
using namespace std;

const int N = 2e5+5;
int cnt , n , m;
queue<int>q[30];
string s , t;

void slove()
{
    cin >> n >> m >> s >> t;
    cnt = 0;
    for( int i = 0 ; i < 26 ; i ++ )
        while( q[i].size() )q[i].pop();
    for( int i = 0 ; i < n ; i ++ )
        q[s[i]-'a'].push(i);
    for( int l = -1 ; ; l = -1 )
    {
        for( int i = 0 ; i < m ; i ++ )
        {
            while( q[T].size() && q[T].front() <= l ) q[T].pop();
            if(q[T].size()) l = q[T].front() , q[T].pop();
            else
            {
                cout << cnt << "\n";
                return ;
            }
        }
        cnt ++;
    }
}

int main()
{
    int ts;
    cin>>ts;
    while(ts--)slove();
    return 0;
}

E 骑士

所有人都不能被其他人秒杀可以转化成,攻击力最高的不能被次高的秒杀,其他人不能被攻击力最高的人秒杀

#include <bits/stdc++.h>
#define ll long long
#define F first
#define S second
using namespace std;

const int N = 2e5+5;
ll k , n , now , last , id;
ll res;
pair<ll,ll> w[N];

void slove()
{
    scanf("%lld" , &n ) , res = 0;
    for( ll i = 1 , x , y , z ; i <= n ; i ++ )
    {
        scanf("%lld%lld%lld" , &x , &y , &z );
        w[i].F = x , w[i].S = y + z;
    }
    sort( w + 1 , w + 1 + n , greater< pair<ll,ll> >() );
    for( int i = 2 ; i <= n ; i ++ )
        if( w[1].F >= w[i].S )
            res += w[1].F - w[i].S + 1;
    if( w[2].F >= w[1].S )
        res += w[2].F - w[1].S + 1;

    printf("%lld\n" , res );
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)slove();
    return 0;
}

F +-串

统计下加减号的数量,一次操作可以让绝对值减二

import math

t = int(input())
while t > 0:
    t -= 1
    s = input()
    k = int(input())
    a = s.count('+')
    b = len(s)-a
    c = abs(a-b)
    if c >= 2*k:
        c -= k*2
        print(c)
    else :
        k -= c//2
        if ( c%2 == 1):
            print(1)
        else:
            print((k%2)*2)

I A+B问题

高精度的模板题目

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5+5;
int k , a[N] , b[N] , n , m ;
string s;


int main()
{
    cin >> k;
    cin >> s;
    n = s.size();
    reverse(s.begin(),s.end());
    for( int i = 1 ; i <= n ; i ++ ) a[i] = s[i-1] - '0';
    cin >> s;
    m = s.size();
    reverse(s.begin(),s.end());
    for( int i = 1 ; i <= m ; i ++ ) b[i] = s[i-1] - '0';
    n = max( n , m );
    for( int i = 1 ; i <= n ; i ++ )
    {
        a[i] += b[i];
        while( a[i] >= k ) a[i+1]++ , a[i] -= k;
    }
    while( a[n+1] ) n ++;
    for( int i = n;i>=1;i--) cout << a[i];
    cout << endl;

}

J 牛妹的数学难题

看似很复杂的问题,实际上因为a的范围只有0,1,2变得很简单

若是有0则一定是零,然后枚举2的数量,用组合数计算出方案数即可,这里的取模中有除法可以直接用费马小定理得到逆元

#include <bits/stdc++.h>
#define ll long long
using namespace std;


const int N = 10000005 , mod = 998244353;
ll n , k , one , two;
ll p[N] , res , tp , cur ;

inline int read()
{
    int x = 0 , 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 ll power( ll x , ll y )
{
    ll ans = 1;
    for( ; y ; y >>= 1 )
    {
        if( y & 1 ) ans = ans * x % mod;
        x = x * x % mod;
    }
    return ans;
}

inline ll invs( ll x )
{
    return power(x , mod - 2 );
}

inline ll C( ll x , ll y )
{
    return p[y] * invs( p[y-x] ) % mod * invs( p[x] ) % mod ;
}

int main()
{

    n = read() , k = read();
    for( int i = 1 , x ; i <= n ; i ++ )
    {
        x = read();
        if( x == 1 ) one ++;
        if( x == 2 ) two ++;
    }
    if( one + two < k )
    {
        cout << 0 << endl;
        return 0;
    }
    p[0] = 1;
    for( ll i = 1 ; i <= max( one , two ) ; i ++ )
        p[i] = p[i-1] * i % mod;

    cur = max( 0LL , k - one );
    tp = power( 2 , cur );
    for( ll i = cur ; i <= min( two , k ) ; i ++ )
    {
        res = ( res + C( i , two ) * C( k - i , one ) % mod * tp ) % mod;
        tp = tp * 2 % mod;
    }
    cout << res << endl;
}
posted @ 2022-02-13 19:52  PHarr  阅读(31)  评论(0编辑  收藏  举报