2020年绵阳CCPC部分题解
比赛链接:https://qoj.ac/contest/1838
D.拆除炸弹
恐怖分子在一栋建筑里放置了一些炸弹!我们的英雄,小马,决定去救建筑里的人。不幸的是,炸弹不止一个,小马无法拆除所有的炸弹。为了争取更多时间让别人逃生,小马决定牺牲自己。
建筑里有 \(n\) 个炸弹,每个炸弹都有一个倒计时钟。一开始,第 $ i $ 个炸弹的时钟设置为$ a_i $。然后:
- 小马选择一个炸弹,使其时钟增加 1。
- 每个炸弹的时钟减少 1。
- 如果至少有一个时钟变为负数,所有炸弹都会爆炸。否则,返回步骤 1。
显然,爆炸是不可避免的。多么悲伤的故事。但小马现在不在乎自己的生存。他只想争取更多时间。那么,你能告诉他在爆炸前最多可以执行步骤 1 多少次吗?
\(Input\)
输入的第一行包含一个整数 \(T\)(\(1 \leq T \leq 100\))——测试用例的数量。
输入的第二行包含一个整数 \(n\)(\(2 \leq n \leq 10^5\))——炸弹的数量。\(n\) 的总和不会超过 \(3 \times 10^5\)。
下一行包含 \(n\) 个数字 \(a_1, a_2, \ldots, a_n\)(\(0 \leq a_1, a_2, \ldots, a_n \leq 10^9\))——炸弹的初始时钟。
\(Output\)
对于第 \(x\) 个测试用例,如果答案是 \(y\),请在一行中输出 \(Case \#x: y\)。
\(Sample\)
2
2
1 1
3
1 2 3
Case #1: 3
Case #2: 4
思路:直接二分存在全都保留的时间,然后得出后再加上一就是答案了,经典二分问题
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
// inline ll read()
// {
// ll x=0,f=1;
// char ch=getchar();
// while(ch<'0'||ch>'9')
// {
// if(ch=='-')
// f=-1;
// ch=getchar();
// }
// while(ch>='0' && ch<='9')
// x=x*10+ch-'0',ch=getchar();
// return x*f;
// }
ll a[250000];
int main()
{
fio();
ll t; ll gs=0;
cin>>t;
while(t--)
{
ll n;
cin>>n;
gs++;
for(ll i=1;i<=n;i++)cin>>a[i];
ll l=0,r=(ll)1e18;
while(l<=r)
{
ll mid=(l+r)>>1;
ll cnt=mid;
priority_queue<ll>q;
ll pd=0;
for(ll i=1;i<=n;i++)
{
if(a[i]-mid>=0)continue;
else
{
if(cnt+a[i]-mid>=0)
{
cnt+=(a[i]-mid);
}
else
{
pd=1;
}
}
}
if(pd)
r=mid-1;
else
l=mid+1;
}
cout<<"Case"<<" #"<<gs<<":"<<" "<<r+1<<endl;
}
}
G.纸牌游戏
小兔子和小马喜欢玩奇怪的纸牌游戏。现在,他们正在玩一种叫做“0123游戏”的纸牌游戏。
桌子上有几张牌。其中 \(c_0\) 张牌标有数字0,\(c_1\) 张牌标有数字1,\(c_2\) 张牌标有数字2,\(c_3\) 张牌标有数字3。小兔子和小马轮流玩游戏,小兔子先开始。在每一轮中,玩家应该选择两张牌,使得这两张牌上的数字之和不超过3,然后用一张标有它们和的牌替换这两张牌。无法进行移动的玩家输掉游戏。
小兔子和小马想知道谁会赢得游戏。
\(Input\)
输入的第一行包含一个整数 \(T\)(\(1 \leq T \leq 10^5\))——测试用例的数量。
每个测试用例包含四个整数 \(c_0, c_1, c_2, c_3\)(\(0 \leq c_0, c_1, c_2, c_3 \leq 10^9\))——分别标有 \(0, 1, 2, 3\) 的牌的数量。
\(Output\)
对于第x个测试用例,如果小兔子赢了,输出一行“Case #x: Rabbit”。否则,输出一行“Case #x: Horse”。
\(Sample\)
2
1 1 1 1
2 2 2 2
Case #1: Horse
Case #2: Rabbit
思路:这题就是打表找规律,打表出来后要分情况讨论,具体也是按着表的规律打出答案的,与模3有关,做这道题做麻了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 1e9+7;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
int main()
{
ll t;
cin>>t;
ll gs=0;
while(t--)
{
gs++;
cout<<"Case #"<<gs<<": ";
ll a,b,c,d;
cin>>a>>b>>c>>d;
ll ans;
if(b==0)
{
if(a)
{
if(c||d)
ans=a%2;
else
{
if(a>=2&&a%2==0)ans=1;
else ans=0;
}
}
else ans=0;
}
else if(c==0)
{
if(a==0)
{
if(b%3==2)ans=1;
else ans=0;
}
else if(a%2==1)
{
if(b==1)ans=1;
else if(b==2)ans=0;
else if(b%3==2)ans=0;
else ans=1;
}
else
{
if(b==1)ans=0;
else if(b==2)ans=1;
else if(b%3==2)ans=1;
else ans=0;
}
}
else
{
if(b%3==1)
{
ans=1;
if(a%2)ans^=1;
}
else if(b%3==2)
{
if(a%2==0)ans=1;
else
{
if(c==1)ans=0;
else ans=1;
}
}
else if(b%3==0)
{
if(a%2==0)
ans=0;
else ans=1;
}
}
cout<<(ans==0?"Horse":"Rabbit")<<endl;
}
}
附赠打表代码:
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 1e9 + 7;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll dfs(ll a, ll b, ll c, ll d, ll sd)//1为r输,0为b输了
{
ll pd1 = 0, pd2 = 0;
if (a == 0)
{
if (b == 0)
return sd % 2;
else if (b == 1)
{
if (c > 0)
{
ll j = dfs(a, b - 1, c - 1, d + 1, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
else return sd % 2;
}
else
{
if (c > 0)
{
ll j = dfs(a, b - 1, c - 1, d + 1, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
ll j = dfs(a, b - 2, c + 1, d, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
}
else
{
if (b > 0 || c > 0 || d > 0)
{
ll j = dfs(a - 1, b, c, d, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
if (b == 1)
{
if (c > 0)
{
ll j = dfs(a, b - 1, c - 1, d + 1, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
}
else if (b >= 2)
{
if (c > 0)
{
ll j = dfs(a, b - 1, c - 1, d + 1, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
ll j = dfs(a, b - 2, c + 1, d, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
}
else if (a >= 2)
{
ll j = dfs(a - 1, b, c, d, sd + 1);
if (j == 1)pd1 = 1;
else pd2 = 1;
}
else return sd % 2;
}
if (sd % 2 == 0)//h想赢
{
if (pd1)return 1;
else return 0;
}
else
{
if (pd2)return 0;
else return 1;
}
}
int main()
{
fio();
ll a, b, c, d;
for (ll i = 2; i <= 2; i++)
{
for (ll j = 0; j <= 0; j++)
{
for (ll k = 1; k <= 10; k++)
{
for (ll d = 0; d <= 0; d++)
{
ll op = dfs(i, j, k, d, 1);
//cout<<i<<" "<<j<<" "<<k<<" "<<d<<" "<<endl;
cout << (op == 0 ? "Rabbit" : "Hourse") << endl;
}
}
}
}
}
J.手工艺的乐趣
小马总是做一些手工艺品,这让他充满了快乐。这次,他建造了一个可以周期性地打开和关闭灯泡的电路。
当然,以下是题目的中文翻译,并且使用了Markdown符号化
电路中有 \(n\) 个灯泡,第 \(i\) 个灯泡有一个周期 \(t_i\) 和一个亮度 \(x_i\)。正式地说,第 \(i\) 个灯泡将在第 \((2kt_i + 1)\) 秒到第 \((2kt_i + t_i)\) 秒之间打开,并且在第 \((2kt_i + t_i + 1)\) 秒到第 \((2kt_i + 2t_i)\) 秒之间关闭,其中 \(k = 0, 1, 2, \ldots\)。当第 \(i\) 个灯泡打开时,它的亮度将是 \(x_i\),否则它的亮度将是 0。
现在,小马想知道,从第一秒到第 \(m\) 秒,所有灯泡中的最大亮度是多少。
\(Input\)
第一行输入包含一个整数 \(T\) \((1 \leq T \leq 100)\) —— 测试用例的数量。
每个测试用例的第一行包含两个整数 \(n, m\) \((1 \leq n, m \leq 10^5)\) —— 灯泡的数量,以及你需要输出的整数数量。\(n\) 和 \(m\) 的总和不会超过 \(2 \times 10^5\)。
接下来的 \(n\) 行,第 \(i\) 行包含两个整数 \(t_i, x_i\) \((1 \leq t_i, x_i \leq 10^5)\) —— 第 \(i\) 个灯泡的周期和亮度。
\(Output\)
第 \(x\) 个测试用例以 \(Case \#x:\) 开始,后面跟着 \(m\) 个整数。第 \(i\) 个整数表示在第 \(i\) 秒时所有灯泡中的最大亮度。如果在第 \(i\) 秒没有灯泡亮着,则输出 0。
\(Sample\)
3
2 3
1 1
2 2
2 5
1 2
2 3
3 3
1 1
1 2
1 3
Case #1: 2 2 1
Case #2: 3 3 2 0 3
Case #3: 3 0 3
思路:首先对于相同的t,我们只要找对应的x最大就好了,然后存储各种t。然后根据t的周期性,枚举各个区间,时间复杂度是\({n/1}+{n/2}+...=nlogn\),然后直接用线段树去区间修改最大值就好了,总时间复杂度\(n*logn*logn\)
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll int
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
const ll maxn = 1e5+5;
struct s
{
ll l, r;
ll ad, v, cf;//ad为加法的懒惰标记,cf为乘法的懒惰标记
}p[maxn << 2];
ll fa[maxn << 2];
void build(ll i, ll l, ll r)
{
p[i].l = l,p[i].r = r,p[i].v = 0;
p[i].ad = 0,p[i].cf = 1;
if (l == r)
{
fa[l] = i;
return;
}
build(i << 1, l, (l + r) >> 1);
build(i << 1 | 1, ((l + r) >> 1) + 1, r);
}
void push_down(ll i)
{
if(p[i].ad)
{
p[i<<1].v=max(p[i<<1].v,p[i].ad);
p[i<<1|1].v=max(p[i<<1|1].v,p[i].ad);
p[i<<1].ad=max(p[i].ad,p[i<<1].ad);
p[i<<1|1].ad=max(p[i].ad,p[i<<1|1].ad);
p[i].ad=0;
}
}
void udq(ll i, ll ad, ll cf, ll l, ll r)//区间修改
{
if (p[i].l == l && p[i].r == r)
{
p[i].v=max(p[i].v,ad);
p[i].ad=max(p[i].ad,ad);
return;
}
push_down(i);
ll i1 = i << 1, i2 = i << 1 | 1;
if (l <= p[i1].r)
{
if (r <= p[i1].r)
udq(i1, ad, cf, l, r);
else
udq(i1, ad, cf, l, p[i1].r);
}
if (r >= p[i2].l)
{
if (l >= p[i2].l)
{
udq(i2, ad, cf, l, r);
}
else
udq(i2, ad, cf, p[i2].l, r);
}
p[i].v = max(p[i1].v,p[i2].v);
}
ll query(ll i, ll l, ll r)
{
ll ans = 0;
if (p[i].l == l && p[i].r == r)
{
ans = p[i].v;
return ans;
}
push_down(i);
ll i1 = i << 1, i2 = i << 1 | 1;
if (l <= p[i1].r)
{
if (r <= p[i1].r)
ans = max(ans , query(i1, l, r));
else
ans = max(ans , query(i1, l, p[i1].r));
}
if (r >= p[i2].l)
{
if (l >= p[i2].l)
{
ans = max(ans , query(i2, l, r));
}
else
ans = max(ans , query(i2, p[i2].l, r));
}
return ans;
}
ll a[250000];
ll b[250000];
int main()
{
fio();
ll t;
cin>>t;
ll gs=0;
while(t--)
{
gs++;
ll n,m;
cin>>n>>m;
ll cnt=0;
build(1,1,m);
for(ll i=1;i<=n;i++)
{
ll x,y;
cin>>x>>y;
if(b[x]==0)
{
cnt++;
a[cnt]=x;
}
b[x]=max(b[x],y);
}
for(ll i=1;i<=cnt;i++)
{
for(ll k=1;k<=m;k+=a[i]*2)
{
ll l=k,r=min(k+a[i]-1,m);
udq(1,b[a[i]],0,l,r);
}
}
for(ll i=1;i<=cnt;i++)
{
b[a[i]]=0;
}
cout<<"Case #"<<gs<<":";
for(ll i=1;i<=m;i++)
{
cout<<" ";
cout<<query(1,i,i);
}
cout<<endl;
}
}
K.知识就是力量
知识就是力量。小兔子和小马都渴望更多的知识,所以他们总是互相出一些谜题。今天,小兔子给小马出了一个新的谜题。
小兔子给小马一个正整数 \(x\)。小马需要找到一个整数集合 \(S = \{a_1, a_2, \ldots, a_n\}\),满足以下条件:
- \(n \geq 2\)
- \(a_i > 1\),对于 \(1 \leq i \leq n\)
- \(\sum_{i=1}^{n} a_i = x\)
- \(a_i\) 和 \(a_j\) 是互质的,对于任何 \(i \neq j\)
例如,如果 \(x = 12\),那么 \(S = \{3, 4, 5\}\) 和 \(S = \{5, 7\}\) 以及 \(S = \{2, 3, 7\}\) 都是有效的集合。两个整数被称为互质,如果唯一正整数能同时整除它们的是1。
我们定义 \(a_{max}\) 为集合 \(S\) 中的最大元素,\(a_{min}\) 为集合 \(S\) 中的最小元素。小兔子希望 \((a_{max} - a_{min})\) 的值尽可能小。你能帮助小马找到 \((a_{max} - a_{min})\) 的最小值吗?
\(Input\)
输入的第一行包含一个整数 \(T\) \((1 \leq T \leq 10^5)\) —— 测试用例的数量。
每个测试用例包含一个整数 \(x\) \((5 \leq x \leq 10^9)\) —— 小兔子给小马的整数。
\(Output\)
对于第 \(x\) 个测试用例,如果答案是 \(y\),请在一行中输出Case #x: y
。如果没有可能的解,请在一行中输出Case #x: -1
。
\(Sample\)
4
5
6
7
10
Case #1: 1
Case #2: -1
Case #3: 1
Case #4: 3
思路:首先特判6错误,然后思考,对于一个奇数来讲,他的最小值应该始终为1,因为一个大于等于5的奇数总能拆成两个相邻的数
1.对于一个偶数来讲,如果它除以二,得出的是一个偶数,则值应该为2,这个显然是对的
2.如果非偶数呢,它的值的最大值应该为4,最小值应该大于2,如14,可为5 9
现在对于3进行特判,可以设第拆分出的第一个为数为x(从小到大)
如果x为奇数,显然答案为x,x+2,x+3为拆分答案,所以有等式\(3*x+5=n\)
如果x为偶数,显然答案为x,x+1,x+3为拆分答案,所以有等式\(3*x+4=n\)
最后进行判定是否存在上述等式成立,如果存在则答案为3否则为4
#include <iostream>
#include <numeric>
#define endl '\n'
#define ll long long
using namespace std;
const ll mod=998244353;
ll qpow(ll x,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*x%mod;
x=x*x%mod;
b>>=1;
}
return ans;
}
ll inv(ll x)
{
return qpow(x,mod-2);
}
ll t,x;
ll gcd(ll x,ll y)
{
return y==0?x:gcd(y,x%y);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>t;
for(int i=1;i<=t;i++)
{
cout<<"Case #"<<i<<": ";
cin>>x;
if(x&1)
{
cout<<1<<endl;
}
else if((x/2)&1)
{
if(x/2/2>1)
{
ll ans=4;
if(x-3>0&&(x-3)%3==0)
{
ll a=(x-3)/3,b=(x-3)/3+1,c=(x-3)/3+2;
if(gcd(a,b)==1&&gcd(a,c)==1&&gcd(b,c)==1)
ans=min(ans,(ll)2);
}
if(x-4>0&&(x-4)%3==0)
{
ll a=(x-4)/3,b=(x-4)/3+1,c=(x-4)/3+3;
if(gcd(a,b)==1&&gcd(a,c)==1&&gcd(b,c)==1)
ans=min(ans,(ll)3);
}
if(x-5>0&&(x-5)%3==0)
{
ll a=(x-5)/3,b=(x-5)/3+2,c=(x-5)/3+3;
if(gcd(a,b)==1&&gcd(a,c)==1&&gcd(b,c)==1)
ans=min(ans,(ll)3);
}
cout<<ans<<endl;
}
else
cout<<-1<<endl;
}
else
{
cout<<2<<endl;
}
}
}