牛客小白月赛18(A(线性筛),B(hash),C(贪心),D(暴力),G(二分加树状数组),H(克鲁斯卡尔最小生成树思维))

本篇博客记录了Forsaken在ACM竞赛中遇到的各类算法题目,包括数论、字符串处理、图论、几何、动态规划等,详细解析了线性筛、Hash、树状数组、最小生成树等算法的实现与应用。

题目链接

A-Forsaken喜欢数论

线性筛水题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N=3e7+10;
int pre[N],p[N];
vector<int>prime;
int n;
void get_p()
{
	int h=0;
	for(int i=2;i<=n;i++)
	{
		if(p[i]==0)
		{
			prime.pb(i);
			pre[i]=i;
		}
		for(int j=0;j<prime.size()&&i*prime[j]<N;j++)
		{
			pre[i*prime[j]]=prime[j];
			p[i*prime[j]]=1;
			if(i%prime[j]==0)  break;
		}
	}
}
ll sum[N];
int main()
{
    scanf("%d",&n);
    get_p();
    ll ans=0;
    for(int i=1;i<=n;++i) ans+=pre[i];
    printf("%lld\n",ans);
}

B-Forsaken喜欢字符串

hash统计一下就可以了。。水题加1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+10;
ll base[3]={43,47};
ll h[3][N][10];
ll mod[3]={1000000007,998244353};
ll f[3][N];
int n,k;
const int M=5e4+10;
char s[M][10];
map<pair<int,int>,int>mp;
void init()
{
    f[0][0]=1;
    f[1][0]=1;
    for(int i=1;i<N;++i)
    {
        for(int j=0;j<2;++j) f[j][i]=f[j][i-1]*base[j]%mod[j];
    }
}
pair<int,int> getv(int id,int l,int r)
{
    pair<int,int>p;
    int d1=(h[0][id][r]-h[0][id][l-1]*f[0][r-l+1]%mod[0]+mod[0])%mod[0];
    int d2=(h[1][id][r]-h[1][id][l-1]*f[1][r-l+1]%mod[1]+mod[1])%mod[1];
    p=make_pair(d1,d2);
    return p;
}
int getid(char c)
{
    return c-'0';
}
int main()
{
    init();
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s[i]+1);
        for(int j=1;j<=k;++j)
        {
            for(int k=0;k<2;++k)
            {
                h[k][i][j]=h[k][i][j-1]*base[k]%mod[k]+getid(s[i][j]);
            }
        }
        for(int r=1;r<=k;++r)
        {
            for(int l=1;l<=r;++l)
            {
                pair<int,int>p;
                p=getv(i,l,r);
                mp[p]++;
            }
        }
    }

    int q;
    scanf("%d",&q);

    while(q--)
    {
        int id,len;
        ll ans=0;
        scanf("%d%d",&id,&len);
        for(int r=1;r<=k;++r)
        {
            for(int l=1;l<=r;++l)
            {
                pair<int,int>p;
                p=getv(id,l,r);
                mp[p]--;
            }
        }
        for(int i=1;i+len-1<=k;++i)
        {
            pair<int,int>p;
            p=getv(id,i,i+len-1);
            //printf("l:%d r:%d mp:%d\n",i,i+len-1,mp[p]);
            ans+=1ll*mp[p]*len;
        }
        for(int r=1;r<=k;++r)
        {
            for(int l=1;l<=r;++l)
            {
                pair<int,int>p;
                p=getv(id,l,r);
                mp[p]++;
            }
        }
    printf("%lld\n",ans);
    }

}
/*
3 4
abab
abab
abab
100
3 1
*/

C-Forsaken给学生分组

排个序,取前k个最大的和前k个最小的放在一起即可。。水题+1

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
int a[N];
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+n);
    ll ans=0;
    for(int i=1,j=n;i<=k;++i,--j){
        ans+=a[j]-a[i];
    }
    printf("%lld\n",ans);
}

D-Forsaken喜欢正方形

注意这里不算规规矩矩的正方形,我想当然了,wa自闭了。

然而这里我判断了三条边的长度是否相等就A了,菱形是hack数据,应该还要判断一个角是90度角

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
struct node
{
    int x,y;
}B[5];
vector<node>a[5];
bool cmp(node a,node b)
{
    if(a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}
int dis(node a,node b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool cal(node a,node b,node c,node d)
{
    node A[5];
    A[1]=a;A[2]=b;A[3]=c;A[4]=d;
    sort(A+1,A+1+4,cmp);
    
    
    if(dis(A[1],A[2])==dis(A[1],A[3])&&dis(A[1],A[2])==dis(A[4],A[2]))
        return 1;
    /*
    if(A[1].x==A[2].x&&A[1].y==A[3].y)
    if(A[2].y==A[4].y&&A[3].x==A[4].x)
        if(A[4].x-A[2].x==A[4].y-A[3].y)
        if(A[4].x-A[2].x==A[2].y-A[1].y)
            return 1;*/
    return 0;
}


int dir[4][2]={1,0,0,1,-1,0,0,-1};
int main()
{
	for(int i=1;i<=4;++i) scanf("%d%d",&B[i].x,&B[i].y);
	if(cal(B[1],B[2],B[3],B[4])) {
        printf("wen\n");
        return 0;
	}

	for(int j=1;j<=4;++j){
        for(int i=0;i<4;++i){
            B[j].x+=dir[i][0];B[j].y+=dir[i][1];
            if(cal(B[1],B[2],B[3],B[4])){
                printf("hai xing\n");
                return 0;
            }
            B[j].x-=dir[i][0];B[j].y-=dir[i][1];
        }
	}
    printf("wo jue de bu xing\n");
}
/*
1 1
2 1
1 3
2 3
*/

G-Forsaken的三维数点

二分加树状数组即可。。。水题+1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n;

int sum[N];

int lowbit(int x)
{
    return x&(-x);
}
void add(int x)
{
    for(int i=x;i<N;i+=lowbit(i)) sum[i]++;
}
int qu(int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i))res+=sum[i];
    return res;
}
int main()
{
    scanf("%d",&n);
    int mx=0;
    for(int i=1;i<=n;++i)
    {
        int op;
        scanf("%d",&op);
        if(op==1){
            ll x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            double dis=sqrt(1ll*x*x+y*y+z*z);
            dis+=(((int)dis)!=dis);
            add((int)dis);
            //printf("dis:%d\n",(int)dis);
        }
        else{
            ll k;
            scanf("%lld",&k);
            int l=0,r=N;
            int ans=-1;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(qu(mid)>=k){
                    ans=mid;
                    r=mid-1;
                }
                else l=mid+1;
            }

            printf("%d\n",ans);

        }
    }


}

H-Forsaken喜欢独一无二的树

这题有点巧妙,来看官方题解:

第一种不知道是什么鬼操作。。

来看第二种。

我只需要加上所有权值相等且之前不在最小生成树内的边,去掉能被最小生成树覆盖的边即可。。。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
typedef long long ll;
struct node
{
    int u,v,w;
}e[N];
int n,m;
int fa[N];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    }
    for(int i=1;i<=n;++i) fa[i]=i;
    sort(e+1,e+1+m,cmp);
    ll ans=0;
    for(int l=1;l<=m;)
    {
        int r=l;
        while(e[r].w==e[l].w&&r<=m)++r;
        --r;
        for(int i=l;i<=r;++i)
        {
            int f1=find(e[i].u);
            int f2=find(e[i].v);
            if(f1!=f2) ans+=e[i].w;
        }
        for(int i=l;i<=r;++i)
        {
            int f1=find(e[i].u);
            int f2=find(e[i].v);
            if(f1!=f2) fa[f1]=f2,ans-=e[i].w;
        }
        l=r+1;

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙大学ccsu_deer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值