【2024秋#113】锦城ACM周测题解

【2024秋#112】锦城ACM周测题解

题目难度

F<B<C<D<E<A

A.推推选妃

思路
先全部遍历一遍,把每个组都遍历一遍存入map

  • b1≠c1和 b2=c2和 b3=c3;
  • b1=c1和 b2≠c2和 b3=c3;
  • b1=c1和 b2=c2和 b3≠c3。

mp1用于存入一个组的第二个数和第三个数
mp2用于存入一个组的第一个数和第三个数
mp3用于存入一个组的第一个数和第二个数
mp4用于存入一个组
然后再遍历一次,ans加上所以符合条件的答案
注意
ans=ans+mp1[{op[i+1],op[i+2]}]+mp2[{op[i],op[i+2]}]+mp3[{op[i],op[i+1]}]-3-(mp4[{{op[i],op[i+1]},op[i+2]}]-1)* 3
因为自身这个组会为mp1,mp2,mp3都贡献了1,所以会减去3,然后考虑会有两个重复的组,就需要再减去(mp4[{{op[i],op[i+1]},op[i+2]}]-1)* 3
故最后求ans就变成了
ans=ans+mp1[{op[i+1],op[i+2]}]+mp2[{op[i],op[i+2]}]+mp3[{op[i],op[i+1]}]-(mp4[{{op[i],op[i+1]},op[i+2]}])* 3;

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void ac()
{
	map<pair<int,int>,int>mp1;
	map<pair<int,int>,int>mp2;
	map<pair<int,int>,int>mp3;
	map<pair<pair<int,int>,int>,int>mp4;
	int n;
	int op[200004];
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>op[i];
	for(int i=1;i<=n-2;i++)
	{
		mp1[{op[i+1],op[i+2]}]++;
		mp2[{op[i],op[i+2]}]++;
		mp3[{op[i],op[i+1]}]++;
		mp4[{{op[i],op[i+1]},op[i+2]}]++;
	}
	ll ans=0;
	for(int i=1;i<=n-2;i++)
		ans=ans+mp1[{op[i+1],op[i+2]}]+mp2[{op[i],op[i+2]}]+mp3[{op[i],op[i+1]}]-(mp4[{{op[i],op[i+1]},op[i+2]}])*3;
	cout<<ans/2<<endl;
}

int main()
{
	int T;
	cin>>T;
	while(T--)
		ac();
	return 0;
}

B.推推想吃猫条

思路
每次后x除以y后,x最少都会减少一半,所以在logx次内一定会到达1。
到达后很好讨论,达到1前按题意模拟即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve()
{
    ll x, y, k;
    cin>>x>>y>>k;
    ll a=y-(x%y);
    while(k>0)
    {
        if(k>=a)
            k-=a;
        else
        {
            x+=k;
            k=0;
            break;
        }
        x+=a;
        while(x%y==0)
        {
            x/=y;
            if(x==1)
            {
                x+=(k%(y-1));
                while(x%y==0)
                    x/=y;
                k=0;
            }
        }
        a=y-(x%y);
    }
    cout<<x<<endl;
}

int main()
{
	int T;
	cin>>T;
	while(T--)
		ac();
	return 0;
}

C.op时间到

思路
先用vector存储无权的有向图,对每个学生分别bfs,用vis[i]表示第i个教室被遍历过多少次,最后只有vis[i]==k的教室满足条件。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll k,n,m,ans;
ll vis[1005];
ll v[1005];
vector<int>niu;
vector<int>op[1005];

void bfs(int x)
{
	queue<int>q;
	q.push(x);
	bool v[1005];
	memset(v,false,sizeof(v));
	v[x]=true;
	while(!q.empty())
	{
		int t=q.front();
		q.pop();
		for(int i=0;i<op[t].size();i++)
		{
			int to=op[t][i];
			if(!v[to])
			{
				v[to]=true;
				q.push(to);
				vis[to]++;
			}
		}
	}
}

int main()
{
	cin>>k>>n>>m;
	for(int i=1;i<=k;i++)
	{
		int t;cin>>t;
		niu.push_back(t);
		vis[t]++;
	}
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		op[a].push_back(b);
	}
	for(int i=0;i<niu.size();i++)
		bfs(niu[i]);
	for(int i=1;i<=n;i++)
		if(vis[i]==k)
			ans++;
	cout<<ans;
	return 0;
}

D.推推是或运算大师

思路
首先求出n在二进制中有多少个1(例如7二进制为111有3个1)
__builtin_popcount()可以返回一个数二进制中1的个数
不过自己写的话也挺简单的hhh
如果只有1个1的话那答案就只有1个是他本身
否则有多个的话假设为k个
那么最终答案有k+1个数
用第四个样例来说
23的二进制为10111
然后23的前一个数和23的或运算为n,也就是把23的二进制的某个1变成0
就可以写出如下
7 19 21 22 23
分别是23减去1,2,4,16
23的二进制1 0 1 1 1
22的二进制1 0 1 1 0
21的二进制1 0 1 0 1
19的二进制1 0 0 1 1
7 的二进制0 0 1 1 1

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

void ac()
{
    ll n;
    cin>>n;
    int ci=__builtin_popcountll(n);
    if(ci==1)
	{
        cout<<1<<endl;
        cout<<n<<endl;
    }
	else
	{
        cout<<ci+1<<endl;
        for(int i=59;i>=0;i--)
		{
            if(n>>i&1)
                cout<<(n^(1LL<<i))<<endl;
        }
        cout<<n<<endl;
    }
}

int main()
{
    int t;
    cin>>t;
    while(t--)
        ac();
    return 0;
}

E.推推打boss

方法1
用一个优先队列去存接下来进行的攻击,按照攻击下次使用的回合从小到大排序,一直跑得到答案

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll atk[200005];
ll cd[200005];
ll aatk[200005];

struct node
{
    ll ATK;
    ll CD;
    ll xiaci;
    bool operator<(const node &other) const
	{
        return xiaci>other.xiaci;
    }
};

void solve()
{
    ll hp,n;
    cin>>hp>>n;
    for(int i=1;i<=n;i++)
        cin>>atk[i];
    for(int i=1;i<=n;i++)
        cin>>cd[i];
    priority_queue<node>pq;
    for(int i=1;i<=n;i++)
        pq.push({atk[i],cd[i],1});
    ll ans=1;
    ll aatk=0;
    while(hp>0)
	{
        vector<node>next;
        aatk=0;
        while(!pq.empty()&&pq.top().xiaci==ans)
		{
            auto node=pq.top();
			pq.pop();
            aatk+=node.ATK;
            node.xiaci+=node.CD;
            next.push_back(node);
        }
        if(aatk==0)
		{
            if(!pq.empty())
			{
                ans=pq.top().xiaci;
                continue;
            }
			else
                break;
        }
        hp-=aatk;
        if(hp<=0)
            break;
        ans++;
        for(auto &node:next)
            pq.push(node);
    }
    cout<<ans<<endl;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
        solve();
    return 0;
}

方法2
先对题目进行分析,如果第k个回合能击败boss,那么k+1回合肯定也可以击败boss.
用二分去枚举回合数

点击查看代码
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;

ll n,m;
ll a[200005],b[200005];

bool check(ll mid)
{
	ll tot=0;
	for(int i=1;i<=m;i++)
	{
        tot+=a[i]*(((mid-1)/b[i])+1);
        if(tot>=n)
			break;
    }
    if(tot>=n)
    	return true;
    return false;
}
void ac()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		cin>>a[i];
	for(int i=1;i<=m;i++)
		cin>>b[i];
	ll l=0,r=1e12,ans=-1;
	while(l+1!=r)
	{
		ll mid=(l+r)/2;
		if(check(mid))
		{
			ans=mid;
			r=mid;
		}
		else
			l=mid;
	}
	cout<<ans<<endl;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
		ac();
	return 0 ;
}

F.猜拳大赛

思路
如果总分不为2的倍数,则输出-1
否则就一直让分数最高的两个人减一分,直到无法操作

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll op[4];

ll ac()
{
	int ans=0;
	cin>>op[1]>>op[2]>>op[3];
	if((op[1]+op[2]+op[3])%2!=0)
		cout<<-1<<endl;
	else
	{
		while(op[2]!=0&&op[3]!=0)
		{
			op[2]--;
			op[3]--;
			ans++;
			sort(op+1,op+3+1);
		}
		cout<<ans<<endl;
	}
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    	ac(); 
    return 0;
}
posted @ 2024-09-13 16:06  怅纡  阅读(143)  评论(0)    收藏  举报