#总结# 1.第一周周五/山东省赛/5h


B - Build Roads

题目

用n-1条路链接n个城市,每个城市有自己的值ai,建路的时候会损失gcd(ai,aj)的值,求最少损失的值是多少
与之前不同的是,会生成随机数,而且数据很大
在这里插入图片描述

思路

一开始看这题觉得最小生成树肯定会超时,但又想不出别的办法,就。。
如果直接打表、或者尝试的话,就会发现,当n很大的时候,结果为n-1,(依据大概是因为素数的分布规律,随着数字增大,素数会增加,gcd就很容易出现1)
然后在打表之后,发现1000之前用最小生成树,1000之后就可以直接n-1输出
还要注意的是,L=R的时候,也是直接输出(n-1)*L

代码

#include <bits/stdc++.h>

#define maxx 201100
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
int n, L, R, a[maxx];
unsigned long long seed;

//create
unsigned long long xorshift()
{
    unsigned long long x = seed;
    x ^= x << 13;
    x ^= x >> 7;
    x ^= x << 17;
    return seed = x;
}

int gen()
{
    return xorshift() % (R - L + 1) + L;
}

int gcd(int a, int b)
{
    while (b)
    {
        int t = b;
        b = a % b;
        a = t;
    }
    return a;
}

//kruskal
int fa[1100];
int cnt = 1;
struct edge
{
    int from, to, l=999999999;
} e[1100];

bool cmp(edge e1, edge e2)
{
    return e1.l < e2.l;
}

void add(int x, int y, int l)
{
    e[cnt].from = x;
    e[cnt].to = y;
    e[cnt].l = l;
    cnt++;
}

int findd(int x)
{
    if (fa[x] != x)
    {
        fa[x]=findd(fa[x]);
    }
    return fa[x];

}

int kruskal()
{
    for (int i = 1; i <= n; ++i)
    {
        fa[i] = i;
    }
    sort(e+1,e+cnt+1,cmp);
    int ans = 0;
    int nn=0;
    for (int i = 1; i <= cnt; ++i)
    {
        int x = e[i].from;
        int y = e[i].to;
        int ll = e[i].l;
        int fx = findd(x);
        int fy = findd(y);
        if (fx!=fy)
        {
            fa[fy] = fx;
            ans+=ll;
            nn++;
        }
        if (nn==n-1)
            break;
    }
    return ans;
}

int main()
{
    scanf("%d%d%d%llu", &n, &L, &R, &seed);
    if (L == R)
    {
        cout << 1ll*(n - 1) * L << endl;
        return 0;
    }
    if (n > 1000)
    {
        cout<<n-1<<endl;
        return 0;
    }
    for (int i = 1; i <= n; ++i)
    {
        a[i] = gen();
       // cout << a[i] << endl;
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = i + 1; j <= n; ++j)
        {
            int len = gcd(a[i], a[j]);
        //    cout<<len<<endl;
            add(i,j,len);
            add(j,i,len);
        }
    }

    int ans = kruskal();
    cout<<ans<<endl;
    return 0;
}

C - Cat Virus

题目

在这里插入图片描述
构造一棵树,满足有k种染色方案

思路

一开始就觉得直接一串结点就可以满足题意了,但是题中n的范围远小于k的范围,肯定是不行的,所以就用最少的结点构造满足条件的树,然后找找规律:
从奇数到偶数:在根节点+根节点
从偶数到奇数:往上提一个与根节点最近的节点
感觉不看题解,想不到,呜呜呜呜

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL k;
int main(){
    cin>>k;
    LL fa=1,id=1,tmp=k,cnt=1;
    while(tmp>3){
        if(tmp&1){
            tmp/=2;
            cnt+=2;
        }
        else{
            tmp--;
            cnt++;
        }
    }
    if(tmp==3) cnt++;//多加一个点 
    cout<<cnt<<endl;
    while(k>3){
        if(k&1){//奇数 
            k/=2;
            id++;
            cout<<fa<<" "<<id<<endl;//左 
            id++;
            cout<<fa<<" "<<id<<endl;//右 
            fa=id;
        }
        else{//奇数 
            k--;
            id++; 
            cout<<fa<<" "<<id<<endl;//右边 
            fa=id;
        }
    }
    if(k==3){
        id++;
        cout<<fa<<" "<<id<<endl;//特判情况,连一个右边 
    }
}


D - Dyson Box

题目

给定一组坐标,放置方块,然后,或者把他们全部向下移动,或者全部向左移动,求每一次放置之后的图形的周长
在这里插入图片描述

思路

一开始想的方法是放一次求一次,找到每次求和的规律,但是后来超时了,一开始其实知道超时,但是不想去想别的思路了,所以浪费了一些时间。
找的规律应该是:每次放上去的周长与上一次周长的关系,这样就不用每次计算了~

代码

#include <bits/stdc++.h>

#define maxx 200100
#define inf 0x3f3f3f3f
using namespace std;
int N;
int vx[maxx], vy[maxx];

int main()
{
    cin >> N;
    int sum1 = 0, sum2 = 0;
    for (int i = 0; i < N; ++i)
    {
        int x, y;
        cin >> x >> y;
        //vertical
        vx[x]++;
        if (vx[x] > vx[x - 1] && vx[x] > vx[x + 1])
        {
            if (vx[x] == 1)
                sum1 += 4;
            else
                sum1 += 2;
        }
        if (vx[x] <= vx[x - 1] && vx[x] <= vx[x + 1])
        {
            if (vx[x] != 1)
                sum1 -= 2;
        }
        if (vx[x] <= vx[x - 1] && vx[x] > vx[x + 1])
        {
            if (vx[x]==1)
                sum1 += 2;
        }
        if (vx[x] > vx[x - 1] && vx[x] <= vx[x + 1])
        {
            if (vx[x]==1)
                sum1 += 2;
        }
        cout<<sum1<<' ';
        //horizontal
        vy[y]++;
        if (vy[y] > vy[y - 1] && vy[y] > vy[y + 1])
        {
            if (vy[y] == 1)
                sum2 += 4;
            else
                sum2 += 2;
        }
        if (vy[y] <= vy[y - 1] && vy[y] <= vy[y + 1])
        {
            if (vy[y] != 1)
                sum2 -= 2;
        }
        if (vy[y] <= vy[y - 1] && vy[y] > vy[y + 1])
        {
            if (vy[y]==1)
                sum2 += 2;
        }
        if (vy[y] > vy[y - 1] && vy[y] <= vy[y + 1])
        {
            if (vy[y]==1)
                sum2 += 2;
        }
        cout<<sum2<<endl;
    }
    return 0;
}

H - Adventurer’s Guild

题目

二维背包

思路

当时觉得自己没学过二维背包就没写。。其实不难的。。

代码

#include <bits/stdc++.h>

#define maxx 1100
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
int n, H, S;
ll f[maxx][maxx];

int main()
{
    cin >> n >> H >> S;
    ll ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        int h, s, w;
        cin >> h >> s >> w;
        for (int j = H; j > h; j--)
        {
            for (int k = S; k >= 0; k--)
            {
                if (j + k > h + s)
                {
                    if (k < s) //stamina不够
                    {
                        f[j][k] = max(f[j][k], f[j - h - s + k][0] + w);
                    }
                    else
                    {
                        f[j][k] = max(f[j][k], f[j - h][k - s] + w);
                    }
                }
            }
        }
    }
    cout << f[H][S] << endl;
    return 0;
}

M - Matrix Problem

题目

矩阵C由矩阵A和B进行运算得到,给出矩阵C,求A,B
AB的运算方式:只有当AB中位置全为1时,C才为1

思路

因为A,B里的1要保证是连通的,一种构造方法是让A的最左边是1,B的最右边是1,然后A奇数行是1,B偶数行是1,就可以保证一定是连通的
主要思维题

代码

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

const int N = 500 + 10;
int n,m;
int A[N][N], B[N][N], C[N][N];


int main()
{
	scanf("%d%d",&n, &m);	
	for(int i = 0;i < n;++i){
		getchar();
		for(int j = 0;j < m;++j){
			char ch = getchar();
			C[i][j] = ch - '0';
		}
	}
	for(int j = 0;j < n;++j){
		A[j][0] = 1;
		B[j][m-1] = 1; 
	}
	for(int i = 0;i < n;++i){
		if(i & 1){
			for(int j = 1;j < m - 1;++j){
				A[i][j] = 1;
			}
		}
		else{
			for(int j = 1;j < m - 1;++j){
				B[i][j] = 1;
			}
		}
	}
	for(int i = 0;i < n;++i){
		for(int j = 0;j < m;++j){
			if(C[i][j] == 1){
				A[i][j] = B[i][j] = 1;
			}
		}
	}
	for(int i = 0;i < n;++i){
		for(int j = 0;j < m;++j){
			printf("%d",A[i][j]);
		}
	}
	for(int i = 0;i < n;++i){
		for(int j = 0;j < m;++j){
			printf("%d",B[i][j]);
		}
	}
	return 0;
}

总结

第一次做5h的题目,感觉自己太菜了,啥也不会做。。
很多时间都在思考题目上,其实代码实现并不复杂
多多做题。。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值