2023牛客寒假算法基础集训营1_20230116「典dp」「典set」「小思维+bfs」「小思维+构造+码力」「位运算博弈(人类智慧)」

 6/13

打得不好,这两天家里也很不好,跟做梦一样,脑子好像被僵尸吃掉了,前两个小时胡乱瞎写交题只过样例,wa了再看,什么b错都能写出来。

M.

M-本题主要考察了找规律_2023牛客寒假算法基础集训营1 (nowcoder.com)

思路:

dp,据说很典。 

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
double dp[550][550];

void work() {
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;++i){
        for(int j=0;j<=m;++j){
            for(int k=0;k<=j;++k){
                dp[i][j]=max(dp[i][j],dp[i-1][j-k]+k*1.0/(m-j+k));
            }
        }
    }
    printf("%.9lf\n",dp[n][m]);
}

signed main() {
	io;
	work();
	return 0;
}

G.

G-鸡格线_2023牛客寒假算法基础集训营1 (nowcoder.com)

思路:

据说也很典。可以线段树、set、并查集。

并查集和set的思路管控下标,打表可以发现该函数会收敛到0/99/100,而且对于数据范围内的数,至多20次我们可以修改到收敛数。我们用set存储所有可以继续收敛的数的下标,在每次查询的时候找到第一个大于l的下标,即第一个需要修改的下标,然后只需要按顺序修改需要修改的东西即可。

ps:还学到了一点set之前没用过的小用法。

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int arr[N];
set<int>se;

int f(int a) {
	return round(10 * sqrt(a));
}

void work() {
	int n, m;
	scanf("%lld %lld",&n,&m);
	int ans = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld",&arr[i]);
		ans += arr[i];
		if (arr[i] != 0 && arr[i] != 99 && arr[i] != 100)
			se.insert(i);
	}
	se.insert(n + 1);
	while (m--) {
		int o;
        scanf("%lld",&o);
		if (o == 1) {
			int l, r, k;
			scanf("%lld %lld %lld",&l,&r,&k);
			int pos = l;
			while (1) {
                int cur = *se.lower_bound(pos);//第一个大于pos的数
				if (cur > r) break;//注意只需要保证下一个需要修改的数比r大即可
                                   //如果用pos>r会超时。
				for (int i = 1; i <= min(k, 20ll); ++i) {
                    /*if (arr[cur] == f(arr[cur])) {
						se.erase(cur);
						break;
					}*/
					ans -= arr[cur];
					arr[cur] = f(arr[cur]);
                    ans += arr[cur];
				}
                if (arr[cur] == f(arr[cur])) {
						se.erase(cur);
				}
				pos=cur+1;
			}
		}
        else {
			printf("%lld\n",ans);
		}
	}
}

signed main() {
	//io;
//yjgg说开了io的话scanf应该是读不进去的,但是这个混过去了,很怪
	work();
	return 0;
}

 F.

F-鸡玩炸蛋人_2023牛客寒假算法基础集训营1 (nowcoder.com)

 思路:

可以证明在一个联通块内无论炸弹在哪里都可以成功放置:当该联通块是树的时候边最少是最严格的情况,而对于每一个树,都可以按dfs序在回溯的时候放炸蛋,于是所有情况都可成立。

只需判断有炸蛋的联通块个数即可:1.0个,所有联通块的大小的平方相加即可。2.1个,该联通块大小的平方。3.大于等于2个,无法跨越联通块所以不存在。

ps:我一开始写的是第2个代码,但是一直有6%的数据过不去,不太懂,换一种写法就过了。放个蹲蹲debug大爹。

//AC

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int f[N], arr[N], son[N];

int find(int x) {
	if (f[x] != x)
		f[x] = find(f[x]);
	return f[x];
}

void join(int a, int b) {
    int aa=find(a),bb=find(b);
	if (aa != bb) {
		son[aa] += son[bb];
		f[bb] = aa;
	}
}

void work() {
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		f[i] = i;
		son[i] = 1;
	}
	while (m--) {
		int a, b;
		scanf("%lld %lld", &a, &b);
		join(a, b);
	}
	int flag = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &arr[i]);
		if (arr[i] != 0) {
            if(!flag){
                flag=find(i);
            }else{
                if(flag!=find(i)){
                    cout<<"0\n";return;
                }
            }
		}
	}
	int ans = 0;
	if (flag) {
        ans=son[flag]*son[flag];
	} else {
		for (int i = 1; i <= n; ++i) {
			if (find(i) == i) {
				ans += son[i] * son[i];
                //cout<<i<<" "<<son[i]<<'\n';
			}
		}
	}
	cout << ans << '\n';
}

signed main() {
	work();
	return 0;
}
//wa_94.23%

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int f[N], arr[N], son[N];

int find(int x) {
	if (f[x] != x)
		f[x] = find(f[x]);
	return f[x];
}

void join(int a, int b) {
    int aa=find(a),bb=find(b);
	if (aa != bb) {
		son[aa] += son[bb];
		f[bb] = aa;
	}
}

void work() {
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		f[i] = i;
		son[i] = 1;
	}
	while (m--) {
		int a, b;
		scanf("%lld %lld", &a, &b);
		join(a, b);
	}
	bool flag = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &arr[i]);
		if (arr[i] != 0) {
			flag = 1;
			arr[find(i)]+=arr[i];//让他爸爸的炸弹数增加
                                 //如果他就是爸爸也没关系,反正我们判的是炸弹数非0。
		}
	}
	int ans = 0;
	if (flag) {
		int tmp = 0;
		for (int i = 1; i <= n; ++i) {
			if (arr[find(i)] != 0 && find(i) == i) {
				ans = son[find(i)] * son[find(i)];
				tmp++;
			}
			if (tmp > 1) {//存在两个有炸弹的连通图
				ans = 0;
				break;
			}
		}
	} else {
		for (int i = 1; i <= n; ++i) {
			if (find(i) == i) {
				ans += son[i] * son[i];
                //cout<<i<<" "<<son[i]<<'\n';
			}
		}
	}
	cout << ans << '\n';
}

signed main() {
	work();
	return 0;
}

 I.

I-本题也主要考察了DFS_2023牛客寒假算法基础集训营1 (nowcoder.com)

思路:

好烦,什么牛马题,输入问题卡了一个多小时。

我们注意到对于任一方阵,牛牛点至多存在1个。

  • 证明:若存在多个,设a,b,c,d中,bc为牛牛点,则由定义可知,a<b<d<c<a,显然不成立。
  • a b
    c d

记录操作2的次数,直接构造即可,并不难写。笨比卡在输入上,因为即使输入时操作2进行了多次,也还是要继续输入完才可以而不能直接return。

        jiangly直接硬搞拓扑真是大爹。

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
bool vis[N];
int a[105][105];
int n, m;

void out0() {
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			cout << "0"<<" \n"[j==n];
		}
	}
}

void work() {
	cin >> n >> m;
	int cnt = 0, sx, sy;
	mem(vis, 0);
	mem(a, 0);
	while (m--) {
		int op;
		cin >> op;
		if (op == 1) {
			int x, y, z;
			cin >> x >> y >> z;
			a[x][y] = z;
			//cout << z << '\n';
			vis[z] = 1;
		} else {
			int x, y;
			cin >> x >> y;
			sx = x, sy = y;
			cnt++;
		}
	}
    if (cnt >= 2) {
		out0();
		return;
	}
	if (cnt == 0) { //没有限制就不用管牛牛点
		int t = 1;
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (a[i][j])
					cout << a[i][j] <<" \n"[j==n];
				else {
					while (vis[t]){
						t++;
                    }
					cout << t <<" \n"[j==n];
					vis[t] = 1;
				}
			}
		}
        return;
	} 

    int l = 1, r = n * n;
    int minn = 0, maxx = inf;
    for (int i = 1; i <= n; ++i) { //处理行最大值
        if (i == sy)
            continue;
        if (!a[sx][i]) {
            while (vis[l]) {
                l++;
            }
            a[sx][i] = l;
            vis[l] = 1;
        }
        minn = max(minn, a[sx][i]);
    }

    for (int i = 1; i <= n; ++i) { //处理列最小值
        if (i == sx)
            continue;
        if (!a[i][sy]) {
            while (vis[r]) {
                r--;
            }
            a[i][sy] = r;
            vis[r] = 1;
        }
        maxx = min(maxx, a[i][sy]);
    }

    if (a[sx][sy]) { //如果牛牛点进行过1操作且最优情况也不符合那就不成立
        if (a[sx][sy] > maxx || a[sx][sy] < minn) {
            out0();
            return;
        }
    } 
    else { //如果牛牛点未被赋过值则在minn和maxx之间给他赋个值
        int tmp = minn + 1;//行的最大值
        while (vis[tmp]) {
            tmp++;
        }
        if (tmp > maxx) {//列的最小值
            out0();
            return;
        }
        a[sx][sy] = tmp;
        vis[tmp] = 1;
    }
    //合理的所有情况按顺序赋值
    int cur = 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (a[i][j]) continue;
            while (vis[cur]) {
                cur++;
            }
            a[i][j] = cur;
            vis[cur] = 1;
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cout << a[i][j] <<" \n"[j==n];
        }
    }
}

signed main() {
	io;
	int t;
	cin >> t;
	while (t--) {
		work();
	}
	return 0;
}

 J.

J-本题竟也主要考察了DFS_2023牛客寒假算法基础集训营1 (nowcoder.com)

思路:

人类智慧||dfs。我还是智慧一点吧感觉dfs也并不是什么无脑东西,不想写。

一些大概有用吧的讨论结果:

00.任意两个数存在4种关系:a严格包含b、b严格包含a、a==b、ab不相交。

1.当我和c不相等&&c!=0时:

如果我严格包含c,则必不可能是由|得到的;如果c严格包含我,则必不可能由&得到。

如果我既不严格包含c也不严格被c包含,那么一定是由^得到。

如果我严格包含c,但对手也无法猜出,则对手一定不是由^得到,此时我可以确定是&;

如果c严格包含我,但对手也无法猜出,则对手一定不是由^得到,此时我可以确定是|。

2.当c==0时:

如果我==0,至少可以确定&可以做到;

如果我!=0,必不可能由|得到,如果对方是0对手先猜到&,如果对方也不是0,那么永远无法猜到,因为我们可以相等也可以取反,这一点无法确定。

3.当我==0时:

如果c==0,我至少可以确定&可以做到;

如果c!=0,此时|和^的操作结果是一样的,随便哪个都可以。

4.当我不包含(c严格包含我||我们不相交)c时:

如果无包含关系则是^;

如果c严格包含a则|一定可以,因为无论是|还是^,在a为0、c为1的位置上b都必须是1,此时至少|一定可以。

//jiangly的讨论,有点乱,但确实情况是全的

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;

void work() {
    int a,b,c;cin>>a>>b>>c;
    int ans;
    if(a==0){
        ans=1;
    }else if((a&c)!=c){//a不包含c(c严格包含a| 或 不相交^) 4.
        ans=1;
    }else if(b==0){
        ans=2;
    }else if(b!=c&&c!=0){//现在情况是a!=0&&a包含c(严格或==)
        //如果b严格包含c则一定是&    1.
        //如果b不包含c则和上述a情况相同   4.
        ans=2;
    }else if(b==c){//如果b==c,则一定是&  (a没猜出来导致b已经得知a的情况,从而可以判断)
        ans=2;
    }else{//现在情况是a!=0&&a包含c(严格或==)
          //b!=c&&c==0                        2.
        ans=-1;
    }
    cout<<ans<<'\n';
}

signed main() {
	io;
	int t;
	cin >> t;
	while (t--) {
		work();
	}
	return 0;
}
//标答的讨论,清晰明了,但赛时不太现实

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;

void work() {
    int a,b,c;cin>>a>>b>>c;
    int ans;
    if(c==0){// 2.
        if(a==0) ans=1;
        else if(b==0) ans=2;
        else ans=-1;
    }else{
        if((a&c)!=c) ans=1;//  4.
        else ans=2;//此时b会知道a==c||a严格包含c  &&c!=0
        //如果b包含c,至少&一定可以
        //如果b不包含c,  2.
    }
    cout<<ans<<'\n';
}

signed main() {
	io;
	int t;
	cin >> t;
	while (t--) {
		work();
	}
	return 0;
}

小小总结

B是优化dp或者组合数,我不会。看jls录屏五分钟内:这是平局,赢了就是这样,输了那就这样,我:哪样?什么?

E计算几何狗都不写。

看完jls的丝滑ak录屏更加觉得自己是答辩了,每天写一些失智东西。

如何评价补八个半小时补6题?下班睡觉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值