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题?下班睡觉。