首页 > 其他分享 >【2023 #84】 锦城ACM周测 (大二个人赛) 题解

【2023 #84】 锦城ACM周测 (大二个人赛) 题解

时间:2023-11-12 14:34:55浏览次数:31  
标签:盒子 fa int 题解 LL ACM 周测 maxn ans

题目难度 \(B<D<E=C<A\)

A: 提高+/省选-
B: 普及-
C: 普及/提高-
D: 普及/提高-
E: 普及/提高-

Candy war

Question

有 \(N\) 个盒子摆成环形,第 \(i\) 和盒子里面有 \(a_i\) 个糖果,他们开始在 \(1\) 好盒子,然后每个人取一次,可以取\(1\), 或者小于当前盒子内糖果数的一个质数 \(p\), 两个人都取了之后就来到下一个盒子取,当有一个人操作前,当前盒子里面没有糖果了,那么这个人就输了

Solution

先考虑 \(N=1\) 的情况,如果 \(a_i=4\) 那么先取的(Cody) 必输,考虑到偶数的质数只有 \(2\) ,那么如果 \(a_i\) 是 \(4\) 的倍数的话,Cody 先取了 \(x\),Loy 取 \((4-x\%4)\) 也就是说,Loy 取了之后,把当前盒子的糖果数控制在 \(4\) 的倍数,那么递归地考虑 \(Cody\) 必输。如果 \(a_i\) 不是 \(4\) 的倍数,Cody 第一步可以取 一个质数 p,使得 \((a_i-p)\%4 == 0\) 这 样子就能把糖果数控制在 \(4\) 的倍数,然后轮到 Loy 取。所以 Loy 必输

因为有很多个盒子,所以对于某一个盒子,必赢的那个人肯定想尽早结束游戏,必输的那个人想慢点结束游戏,可得

  • 必赢的人第一次取最大的 \(p\), 使得 \((a_i-p)\% 4==0\),这样子才能赢的快
  • 必输的那个人每次肯定取 \(2\) ,这样导致必赢的那个人只能也取 \(2\),这样子才能输的慢

由此推知每个盒子 \(a_i\) 要取的次数为 \(F_i=(a_i-p) /2\) ,要取的轮数是 \(F_i /2\) 这个数,也是他们两个转的圈数,当转到某一圈时当前盒子的糖果数为 \(0\) 时,这个盒子必输的那个人对于整个游戏输了。

取的轮数最少的那个盒子就是第一个取完的盒子

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e6+5;
bool vis[maxn];
int min_turn[maxn]={0,1},max_mod4[4]={2,1,2,3};
inline void init(){
    for(int i=2;i<maxn;i++){
        if(!vis[i]){  //i是一个质数
            for(int j=1;j*i<maxn;j++)
                vis[i*j]=1;
            max_mod4[i%4]=i;  // max_mod4[]记录的是最大的 p,使得 (ai-p) % 4 ==0
        }
        if(i&1)min_turn[i]=(i-max_mod4[i%4])/2+1;  
        else min_turn[i]=i/2;
    }
}
int main(){
    init();
    int T;cin>>T;
    while(T--){
        int N;cin>>N;
        int ans=maxn;
        for(int i=0;i<N;i++){
            int now;cin>>now;
            if(min_turn[now]/2<ans/2) //min_turn 是转的圈数,注意这里的 /2 不能约掉
                ans=min_turn[now];
        }
        printf("%s\n",ans&1?"Cody":"Loy");
    }
    return 0;
}

Tuition dispute

Question

有 \(N\) 个学生,每个学生可以接受的最高学费为 \(a_i\) ,我们要设置一个学费值,使得收到的总的学费最多

Solution

贪心的思想,把学费从高的 \(a_i\) 起往下降,此时收到的学费值是 学分值 \(\times\) 人数,求最大值就好了

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
int N;
LL a[maxn];
LL ans=0,money;
int main(){
    // freopen("A.in","r",stdin);
    cin>>N;
    for(int i=1;i<=N;i++)
        cin>>a[i];
    sort(a+1,a+1+N);
    for(int i=N;i;i--){
        if(a[i]*(N-i+1)>=ans)
            ans=a[i]*(N-i+1),money=a[i];
    }
    cout<<ans<<" "<<money<<endl;
    return 0;
}

Pet

Question

有两个种类的狗,简称 G 和 H,给出 \(a_i\) G/H 表示第 \(i\) 个位置站的是什么类型的狗,每个狗最多往左或者右走 \(k\) 步,你需要在一些位置放上狗的食物,求放的食物最少的数量,并给出一种可行的方案

Solution

考虑到每一只狗向左或者向右走的步数是一样的,那么考虑贪心,从前往后处理,对于一只没有食物吃的狗,那么我们在 \([i-k,i+k]\) 的范围内放食物都可以,考虑到后面处理的狗的初始位置都大于 \(i\) 所以这种食物肯定往右边放好,那么就放在 \(i+k\) 的位置,如果超过了 \(N\) 那么就放在 \(N\)

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);

	int T;
	cin >> T;

	while (T--) {
		int n;
		int k;
		cin >> n >> k;
		string s;
		cin >> s;
		int patchG = -k - 1; 
		int patchH = -k - 1; 
		int cnt = 0;
		string ans(n, '.');
		for (int i = 0; i < n; i++) {
			if (s[i] == 'G' && i - patchG > k) {
				// 最近的 G不能覆盖 i
				++cnt;
				if (i + k >= n) { //如果 超过N 了
					patchG = n-1;
					if (ans[patchG] == 'H') { patchG--; }
				} else {
					patchG = i + k; // i+k的位置放上 G
				}
				ans[patchG] = 'G';
			}
			if (s[i] == 'H' && i - patchH > k) {
				++cnt;
				if (i + k >= n) {
					patchH = n-1;
					if (ans[patchH] == 'G') { patchH--; }
				} else {
					patchH = i + k; 
				}
				ans[patchH] = 'H';
			}
		}
		cout << cnt << endl << ans << endl;
	}
    return 0;
}

SCM

Question

给出一个由 #. 组成的矩阵,其中我们假设 # 代表一个传感器,如果一个传感器的相邻 \(8\) 个各自内有另外一个传感器,那么我们称这两个传感器是关联的,而且这种关系性是可以传递的,相互关联的传感器为一组,问一共有几组传感器

Solution

使用并查集,把相邻的传感器看成是一个祖先的,然后查询有几个祖先就好了

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int H,W,g[maxn][maxn],fa[maxn*maxn],tot,ans,hsh[maxn*maxn];
char mp[maxn][maxn];
const int flg[8][2]={{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
int getfa(int x){
    return (fa[x]==x)?x:fa[x]=getfa(fa[x]);
}
int main(){
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);
    scanf("%d%d\n",&H,&W);
    for(int i=1;i<=H;i++)scanf("%s",mp[i]+1);
    for(int i=1;i<=H;i++)
    for(int j=1;j<=W;j++){
        g[i][j]=++tot;
        fa[tot]=tot;
    }
    for(int i=1;i<=H;i++)
    for(int j=1;j<=W;j++)if(mp[i][j]=='#'){
        for(int k=0;k<8;k++)if(mp[i+flg[k][0]][j+flg[k][1]]=='#'){
            int fx=getfa(g[i][j]),fy=getfa(g[i+flg[k][0]][j+flg[k][1]]);
            if(fx==fy)continue;
            fa[fx]=fy;
        }
    }
    
    for(int i=1;i<=H;i++)
    for(int j=1;j<=W;j++)if(mp[i][j]=='#'){
        int F=getfa(g[i][j]);
        if(hsh[F]==0) ans++,hsh[F]=1;
    }
    printf("%d\n",ans);
    return 0;
}

Smartest child

Question

给出两个长度为 \(M\) 的最大值为 \(N\) 的序列 \(S,T\) , 我们需要构造一个长度为 \(N\) 的由 0/1 组成的序列 \(X\),若能构造出满足 \(X_{S_i} \ne X_{T_i}\) 的 \(X\) 那么称 \((S,T)\) 是 XiaoMo favourite sequence ,问是否可以构造出来

Solution

我们可以把 \(X\) 的 0/1 看成是对点的分类,0为一类,1为一类,然后 \((S,T)\) 相当于对这些点建边,给出了一些边,如果我们能对 \(X\) 分成一个二分图的话,那么就是 yes

那么就有几种判定二分图的方法

我们可以先给图建边,然后dfs给点染色,每一层的颜色和上一层相反(0->1,1->0)

如果存在矛盾点,那么就不能染色成功,就是 No

第二种方法就是 分类 并查集,如果两个点一个是 \(0\) 一个是 \(1\) 那么我们就把 \(i\) 和 \(j+N\) 连边,\(j\) 和 \(i+N\) 连边,正常处理就好了

洛谷题解链接

Code

染色

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int N,M,a[maxn],b[maxn];
vector<int> E[maxn];
int vis[maxn];

void dfs(int x,int val){
    vis[x]=val;
    int len=E[x].size();
    for(int j=0;j<len;j++){
        int son=E[x].at(j);
        if(vis[son]==val) {printf("No\n");exit(0);}
        if(!vis[son])dfs(son,-val);
    }
}
int main(){
    freopen("D.in","r",stdin);
    cin>>N>>M;
    for(int i=1;i<=M;i++)cin>>a[i];
    for(int i=1;i<=M;i++)cin>>b[i];
    for(int i=1;i<=M;i++){
        E[a[i]].push_back(b[i]);
        E[b[i]].push_back(a[i]);
    }
    for(int i=1;i<=N;i++)if(!vis[i]){
        dfs(i,1);
    }
    printf("Yes\n");
    return 0;
}

分种类并查集

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int N,M,a[maxn],b[maxn];
int fa[maxn<<1];
int getfa(int x){
    return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
void merge(int x,int y){
    int fx=getfa(x),fy=getfa(y);
    if(fx!=fy) fa[fx]=fy;
}
int main(){
    freopen("D.in","r",stdin);
    cin>>N>>M;
    for(int i=1;i<=(N<<1);i++) fa[i]=i;
    for(int i=1;i<=M;i++) cin>>a[i];
    for(int i=1;i<=M;i++) cin>>b[i];
    for(int i=1;i<=M;i++){
        int fx=getfa(a[i]),fy=getfa(b[i]);
        if(fx==fy) {printf("No\n");return 0;}
        merge(a[i],b[i]+N);
        merge(b[i],a[i]+N);
    }
    printf("Yes\n");
    return 0;
}

记录路径并查集

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=3e5+5;
LL n,m,a[N],b[N],fa[N],to[N];
LL find(LL x)
{
	if(fa[x]==x)return x;
	LL t=find(fa[x]);
	to[x]+=to[fa[x]];
	return fa[x]=t;
}
int main()
{
    freopen("D.in","r",stdin);
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&b[i]);
	}	
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		LL fx=find(a[i]),fy=find(b[i]);
		if(fx==fy)
		{
			if((to[a[i]]+to[b[i]])%2==0)
			{
				puts("No");
				return 0;
			}
		}
		else
		{
			fa[fx]=fy;
			to[fx]=1-(to[a[i]]+to[b[i]])%2;
		}
	}
	puts("Yes");
}

标签:盒子,fa,int,题解,LL,ACM,周测,maxn,ans
From: https://www.cnblogs.com/martian148/p/17827148.html

相关文章

  • chatgpt的api联网报错问题解决:openai公司的api联网报错解决
    chatgpt是啥,这里不讲,openai是啥这里也不讲。要知道我们不论是通过网页web使用chatgpt还是使用api方式通过客户端使用chatgpt都是需要使用外国IP的,     ......
  • 「题解」P6791 [SNOI2020] 取石子
    anti-game没有用,能取到\(n-1\)的必胜,不能取到\(n-1\)的必败,所以现在考虑取走最后石子获胜的情况。对于一个\(n\)来说合法的\(k\)一定是一个前缀,并且一定是贪心取最小的(留给对方的机会更小),所以启发将每个\(n\)最小的合法的\(k=a_n\)打出表来,找到最小的\(j\)满足\(......
  • 【洛谷 P2669】[NOIP2015 普及组] 金币 题解(循环)
    [NOIP2015普及组]金币题目背景NOIP2015普及组T1题目描述国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币……;这种工资发放模式会一直这......
  • ABC 328 题解
    A直接模拟即可。cin>>n>>m;for(inti=1;i<=n;++i){ intx;cin>>x; if(x<=m)sum+=x;}cout<<sum;B直接模拟即可。intn,ans;boolchk(intx,inty){ intdig=x%10;x/=10; while(x){ if(x%10!=dig)return0; x/=10; } while(y){ if(y%10......
  • ABC328F 题解
    blog。提供一个普通并查集+启发式合并做法。考虑直接维护\(X_i\)。对于\(X_u-X_v=w\),分四种情况。\(X_u,X_v\)都没被维护过。直接钦定\(X_u\getsw,X_v\gets0\),以后再改。\(X_u\)没被维护过,\(X_v\)被维护过。显然\(X_u\getsX_v+w\)。\(X_v\)没被维护过,\(X_u\)被......
  • ABC328G 题解
    blog。剩下几分钟的时候胡出来了,但是时间不够,痛失AK/dk。\(N\le22\),显然状压DP。\(dp_s\)表示确定\(s\)集合的元素所需的代价(这些元素都放在最前面)。确定了\(s\)后,发现会有\(\operatorname{popcount}(s)\)个元素堆在前面,那么枚举\([L,R]\)接在后面,也就是\([\opera......
  • [题解] CF407E k-d-sequence
    k-d-sequence给你一个长为\(n\)的序列,求最长的子区间使得它加入至多\(k\)个数后,重排后是公差为\(d\)的等差数列。\(n,k\le2\times10^5\),\(0\led\le10^9\)。公差是\(d\)的等差数列模\(p\)的值应该相等,所以把序列按极长模\(p\)同余的连续段分组。对于同......
  • [题解] CF505E Mr. Kitayuta vs. Bamboos
    Mr.Kitayutavs.Bamboos给定\(n\)个数\(h_{1\dotsn}\)。你需要进行\(m\)轮操作,每轮操作为\(k\)次修改,每次修改可以选择一个数\(h_i\)修改为\(\max(h_i-p,0)\)。每轮操作后每个\(h_i\)将会被修改为\(h_i+a_i\)。你需要最小化最终\(h_{1\cdotsn}\)中......
  • 【题解 P8763】[蓝桥杯 2021 国 ABC] 异或变换
    同楼上dalao做法:#include<iostream>#include<algorithm>#include<cstdio>#include<cmath>#include<cstring>#include<string>#include<cstdlib>#include<bitset>usingnamespacestd;constintN=1e4+10......
  • ABC328题解(C-G)
    A/B较为简单,略去。C预处理一下,然后前缀和就好了。时间复杂度\(O(n)\)。D用链表来记录字符串。注意到每次能够消去意味着链表上连续三个节点拼起来是ABC,然后从左到右一个个算就行了。匹配到的话把三个节点一起删掉。时间复杂度\(O(n)\)。E注意到\(N\le8,M\le28\)......