2021牛客多校1补题记录

本文作者分享了参与算法竞赛的心得,涉及博弈论问题、几何图形分析以及数论应用。针对不同题目,作者分别阐述了各自的解题思路和代码实现,包括石子博弈、等腰梯形与球的问题、01矩阵变换、3友好数的规律以及数组重排。此外,还探讨了哈希冲突最小化和大规模数组排序的策略。
摘要由CSDN通过智能技术生成
总结

这场打的还可以,不算那么自闭。
开局我运气好读到签到题,极速签到,然后挂机。
郎老师发现了F的规律,凯瑞打了个A的表。
B初中数学wa太多了,罚时上天,很可惜。
赛中敲了个I,一直T,以为卡常,最后才发现复杂度假了。赛后题解看不懂,神仙DP。权当练下概率DP了,等两天如果有看得懂的题解再补。
H好像是fft板子,等点了技能点再来补,K也需要点技能点,等点完了来补。

A 题意

博弈,两堆石子,从1堆取a个,另一堆取ka个,k可以为0,a大于0.取不了判负,给出石子数,求胜负

A 思路

打表。必败态其实不多,跟n是一个数量级的。利用类似筛法的方式,筛出所有必败态就行。代码没有放完。

A 代码
#include<stack>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<deque>
#include<vector>
#include<iostream>
#include<map>
#include<set>
#include<iomanip>
using namespace std;
const int N = 5010;
bool mp[N][N];
int T,n,m;
signed main() 
{
    mp[2][3]=1;
mp[5][7]=1;
mp[9][12]=1;
cin>>T;
while (T--)
{
    cin>>n>>m;
    if (n>m) swap(n,m);
    if (mp[n][m]) cout<<"Bob"<<endl;
    else cout<<"Alice"<<endl;
}
    return 0;
}
B 题意

给出一个等腰梯形参数,一个球,问是否从大口扔进去会卡住。

B 思路

初中数学,相似就好了,wa太多了。
这里我写出正解后居然因为没加stuck wa1,心态和代码能力还是不行,需要历练

B 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k,q;
	double r,a,b,h;
	void solve(){
		cin>>r>>a>>b>>h;
		if(b>=2*r){
			cout<<"Drop"<<endl;
			return ;
		}
		double x=b*h/(a-b);
		double w=sqrt((h+x)*(h+x)+a*a/4.0);
		double ans=w*2.0*r/a-x;
		cout<<"Stuck"<<endl;
		cout<<fixed<<setprecision(10)<<ans<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		while(tn--){
			solve();
		}
	} 
						
D 题意

01矩阵,问有多少方式把一个连续0串变成一个连续的1串,1串长度固定。

D 思路

cf a题水平,统计所有连续0串长度,给出的1串长为n的话,对max(0,len-n+1)求和就好了,len是0串长

D 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=2505;
	const int inf=0x3f3f3f3f;
	int n,m,k,q;
    char a[maxn][maxn],b[maxn];
	void solve(){
		cin>>n>>m;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)   
                cin>>a[i][j];
        for(int i=1;i<=n;i++)
            a[i][n+1]='1';
        for(int i=1;i<=m;i++)   cin>>b[i];
        int ans=0;
        for(int i=1;i<=n;i++){
            int cnt=0;
            for(int j=1;j<=n+1;j++){
                if(a[i][j]=='1'){
                    ans+=max(0,cnt-m+1);
                    cnt=0;
                }
                else{
                    cnt++;
                }
            }
        }
        cout<<ans<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		while(tn--){
			solve();
		}
	} 
						
F 题意

1个数3友好是说他的连续子串是3的倍数,给出lr求l到r之间3友好数个数

F 思路

lr是1e18的,所以几乎否决了除了找规律以外的一切方法。打表发现90以后所有数都是3友好的,预处理一下直接输出就行了。

F 代码
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#pragma G++ optimize(2)
#define endl "\n"
#define online_judge
#define debug(x) cout << "debug: " << x << endl;
using namespace std;
typedef long long ll;
const int maxn = 90;

int cnt[maxn];
signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
#ifndef online_judge
    freopen("IO\\in.txt", "r", stdin);
    freopen("IO\\out.txt", "w", stdout);
#endif
    vector<int> vec{3, 6, 9, 10, 12, 13, 15, 16, 18, 19, 20, 21, 23, 24, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 45, 46, 48, 49, 50, 51, 53, 54, 56, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 72, 73, 75, 76, 78, 79, 80, 81, 83, 84, 86, 87, 89};
    for (int i = 1; i <= 89; ++i) {
        int pos = upper_bound(vec.begin(), vec.end(), i) - vec.begin();
        cnt[i] = pos;
    }
    int t;
    cin >> t;
    while (t--) {
        ll a, b;
        cin >> a >> b;
        if (a > 89)
            cout << b - a + 1 << endl;
        else if (b <= 89)
            cout << cnt[b] - cnt[a - 1] << endl;
        else
            cout << b - 89 + cnt[89] - cnt[a - 1] << endl;
    }
    return 0;
}
K 题意

给一个数组,让你重新排列,使得
∑ i = 0 n − 1 ∣ a i − b i ∣ \sum \limits_{i=0}^{n-1} \sqrt{|a_i-b_i|} i=0n1aibi
最小。a为数组,b为下标(0开始)
因为n比较大(1000),不要求一定最优,差距小于一定百分比即可
s数组数据完全随机

K 思路

乱搞题。既然是较优解,可以直接类似冒泡排序一样进行操作,大概整个十几次就能过了。

K 代码
#include<cstdio>
#include<iostream>
#include<set>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#include<bitset>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=1505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int a[maxn];
    double sq[maxn];
	void solve(){
        cin>>n;
        for(int i=0;i<n;i++)   cin>>a[i];
        for(int k=1;k<=10;k++){
            for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++){
                if(sq[abs(i-a[i])]+sq[abs(j-a[j])]>sq[abs(i-a[j])]+sq[abs(j-a[i])])
                    swap(a[i],a[j]);
            }
        }
        for(int i=0;i<n;i++)   cout<<a[i]<<' ';
        cout<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
        for(int i=1;i<maxn;i++)
            sq[i]=sqrt(i);
        cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
H 题意

n个数哈希,策略是直接模一个数。求最小的不冲突模数
范围0-50w

H 思路

冲突时当且仅当|ai-aj|%m=0
换句话说,m不能是任何一对aiaj的约数,数的范围不大,如果我们能知道所有|ai-aj|,那么我们枚举m,判断下他每一个倍数有没有出现过,就可以判断m是否可以做答案。这个复杂度是调和级数,nlogn级别的。
下面问题在于我们如何知道所有的ai-aj。这里需要一个前置知识。我们把ai看作多项式f1中x^ai
的系数,aj同样处理。那么所有ai+aj可以通过对两个多项式进行卷积得出。(显然,卷积后如果 x^k 的系数不为0,说明有两项相乘为k的情况,根据多项式的生成方式,也就是ai+aj=存在)。那么如果是ai-aj呢?不难发现可以把aj放在x^-aj的系数上。但fft不支持这种操作,那我们可以整体给他上一个偏移量d,似的所有d-aj>=0,我们卷积完之后次数全部减d就可以了。

H 代码
#include<cstdio>
#include<iostream>
#include<set>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#include<bitset>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=2100505;
	const int inf=0x3f3f3f3f;
    const int mod=998244353;
	const int g=3;//原根
	int n,m,k;
    int a[maxn],b[maxn]; 
	int rv[maxn];
	int len,cnt;
	int binpow(int a,int b=mod-2){
		a%=mod;
		int res = 1;
		while(b>0){
			if(b&1)
				res=res*a%mod;
			a=a*a%mod;
			b>>=1;
		}
		return res%mod;
	}
	void change(){
		for(int i=0;i<len;i++)
        	rv[i]=(rv[i>>1]>>1)|((i&1)<<(cnt-1));
	}
	void NTT(int *c,int type){
		for(int i=0;i<len;i++)
			if(i<rv[i])	
				swap(c[i],c[rv[i]]);
		for(int mid=1;mid<len;mid<<=1){
			int wn=binpow(g,(mod-1)/(mid*2));
			if(type==-1)	wn=binpow(wn);
		    for(int R=mid<<1,j=0;j<len;j+=R){
				int w=1;
            	for(int k=0;k<mid;k++,w=(w*wn)%mod){
					int x=c[j+k],y=w*c[j+mid+k]%mod;
                	c[j+k]=(x+y)%mod;
                	c[j+mid+k]=(x-y+mod)%mod;
            	}
        	}
    	}
		if(type==-1){
            int re=binpow(len);
        	for(int i=0;i<len;i++)
				c[i]=c[i]*re%mod;
        }
	}
	void solve(){
        cin>>n;
        while(n--){
            int t;
            cin>>t;
            a[t]++;
            b[500005-t]++;
        }
        len=1,cnt=0;
        while(len<=1000000)  len*=2,cnt++;
        change();
        NTT(a,1);NTT(b,1);
        for(int i=0;i<=len;i++){
            a[i]=1ll*a[i]*b[i]%mod;
        }
        NTT(a,-1);
        bool ok=1;
        for(int i=1;;i++){
            ok=1;
            for(int j=i;j<500005;j+=i){
                if(a[j+500005]){
                    ok=0;
                    break;
                }
            }
            if(ok){
                cout<<i<<endl;
                return ;
            }
        }
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		//cin>>tn;
		for(int __=1;__<=tn;__++){
			solve();
		}
	} 
	
						
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值