2020 - S -题解
今年的题着实令人着迷,\(CSP\)挂了,\(NOIP\) 还是要打,本不想碰今年的这个题了,还是拾起了自己摔碎的破烂罐子。
T1 儒略日
利用二分求出年份来,然后就用总天数 \(sum\)减去到该年的总天数,然后就一个月一个月的减去就行
对于某些特判(就是我犯的):
1. 判断是否为闰年,公元零年没有,也就是公元 1 年, 在\(4713≡1(\)\(mod\) \(p)\),所以很坑人的就是\(4713 BC\)是闰年
2.在判断 1582年的时候,并不是\(10\)天,而是\(11\)天
代码:
正解:
/*儒略历*/
/*
知识点: 模拟,二分,数论
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
typedef long long ll;
using namespace std;
inline int read()
{
int 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<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline ll readll()
{
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<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
int mouth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
//定义每个月份的天数,闰年的话总天数加上一天就行了
int y=-4713,m=1,d=1;//定义年月日,在每一次询问之前都需要进行初始化
int judge(int y)//判断是否为闰年
{
if(y<=0)
{
y=-y;
return y%4==1;//这个就很气人
}
if(y<=1582) return y%4==0;//如果小于1582那么就是按照儒略日,其他的按照格里高利历
return (y%400==0)||((y%4==0)&&(y%100!=0));
}
void getday()
{
d++;
if(y==1582&&m==10&&d==5)//在1582年凭空消失11天,需要特判一下
{
d=15;
}
if(d>mouth[m]+(m==2&&judge(y)))//如果今年是闰年,那么二月份为29天
{
m++,d=1;//天数已经超过这个月份的天数上限,那么就需要进一个月,是个人就知道
}
if(m>12)y++,m=1;//和上面一样
}
//就是注意一下long long
ll checkday(int x)//判断下 以 x年到 BC 4713年的总天数
{
ll day=1ll*(x+4712)*365;
if(x>1582)day-=10;
day+=1ll*(x+4712+3)/4;
if(x>=1600)
{
x-=1601;
day-=(ll)x/100ll;//100的倍数不是闰年
day+=(ll)x/400ll;//400的倍数是闰年,上面减多了,(十分类似于2个集合的容斥原理)
}
return day;
}
int main()
{
int T;
T=read();
while(T--)
{
y=0;m=d=1;
ll n=readll();
int l=-4712,r=1e9+1;
while(l<=r)
{
int mid=(l+r)/2;
if(checkday(mid)<=n)//代表mid年到BC 4713年的天数要小于总天数
{
y=mid;
l=mid+1;
}
else r=mid-1;
}
n-=checkday(y);
if(y<=0)y--;
while(n--)getday();
printf("%d %d ",d,m);
if(y<=0)printf("%d BC\n",-y);
else printf("%d\n",y);
}
return 0;
}
暴力:
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <cstring>
#define LL long long
const int N = 100000 + 10;
const LL D[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
LL d = 1, m = 1, y = -4713;
LL read() {
LL f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = 10 * w + ch - '0';
return f * w;
}
void SolveBC()
{
++ d;
if (d > D[m]) {
if (m == 2 &&
d == 29 &&
(- y - 1ll) % 4 == 0) {
int koishi;
} else {
++ m;
d = 1;
}
}
if (m > 12) {
++ y;
m = 1;
}
if (y == 0) y = 1;
}
void Solve1()
{
++ d;
if (d > D[m]) {
if (m == 2 &&
d == 29 &&
y % 4 == 0) {
int koishi;
} else {
++ m;
d = 1;
}
}
if (m > 12) {
++ y;
m = 1;
}
if (5 == d && m == 10 && y == 1582) {
d = 15;
}
}
void Solve2() {
bool yes = false;
if (y % 400 == 0) yes = true;
if (y % 4 == 0 && y % 100 != 0) yes = true;
++ d;
if (d > D[m]) {
if (m == 2 && d == 29 && yes) {
int koishi;
} else {
++ m;
d = 1;
}
}
if (m > 12) {
++ y;
m = 1;
}
}
int main()
{
int T;
scanf("%d", &T);
while (T --) {
LL r = read();
{
d = 1, m = 1, y = -4713;
while (r --) {
if (y < 0) SolveBC();
else if (y <= 1582) Solve1();
else Solve2();
}
if (y < 0) {
printf("%lld %lld %lld BC\n", d, m, -y);
} else {
printf("%lld %lld %lld\n", d, m, y);
}
continue ;
}
}
return 0;
}
T2 动物园
总共有 \(2^k\)种动物,其中动物园有n种,(动物园\(TM\)缺动物\(?\),怎么想的)最后的结果那么肯定就是所有的饲料能够支撑多少种动物\(2^x\)减去\(n\)就行了,(啥叫还,我一开始就直接输出了\(2^x\),开心的暴了5分)
至于怎么求,通过位运算符 \(|\)来求,先定义一个 f=0,表示现在啥饲料没有(穷死),与动物园的饲料\(|\)上,然后就得出了动物园种现在的饲料,那么对于要求的时候,就发现,如果\(f\)的第\(p\)位为1,那么能饲养这种动物
代码:
/*动物园*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#define int long long
using namespace std;
const int maxn=1e6;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int f=0;
bool vis[maxn];
signed main()
{
int n=read(),m=read(),c=read(),k=read();
for(int i=1;i<=n;i++)
{
int a=read();
f=f|a;
}
for(int i=1;i<=m;i++)
{
int p=read(),q=read();
if((f|(1ull<<p))!=f && !vis[p] )//表示动物园中需要但是没有这种的饲料
{
k--;
vis[p]=true;
}
}
cout<<"k:"<<k<<endl;
if(k==64 && n==0) //特判一下,刚好long long溢出个一
{
cout<<"18446744073709551616"<<endl;
return 0;
}
unsigned long long ans=(unsigned long long)pow(2,k)-n;
cout<<ans<<endl;
return 0;
}
T3 函数调用
待到T4全绿时,便是T3题解日;
T4 贪吃蛇
作为第一道黑题(但是我觉得这就是黑题,未免有些……)
分析一下题意:
有多条
在吃(这玩意是真的,蛇真吃同类),然后一回合只能吃一条,那就是最大的吃掉最小的(我就没看题,直接模拟本来20分,然后\(0.0\)),如果还能吃那么放在下一回合去;
20分做法:
就是判断一下,最长的蛇是否可以吃掉最小的蛇,什么时候不能吃掉,就是不满足题意呗
int main() {
int T = read();
for (int nowT = 1; nowT <= T; ++ nowT) {
if (nowT == 1) {
n = read();
for (int i = 1; i <= n; ++ i) {
ori[i] = a[i] = (Sna) {i, read()};
}
} else {
int k = read();
for (int i = 1; i <= k; ++ i) {
int p = read();
ori[p].a = read();
}
for (int i = 1; i <= n; ++ i) {
a[i] = ori[i];
}
}
int ans = n;
if (n == 3)
{
if (a[3].a - a[1].a >= a[2].a) {
ans = 1;
} else {
ans = 3;
}
printf("%d\n", ans);
continue ;
}
}
return 0;
}
70分做法:
我们直接就认为这些蛇已经冲昏了头脑,如果能吃,它将往死里吃(确实能把自己吃死呀!\(QwQ\))并且在吃的时候记录一下这里面的蛇的数量,吃完之后,我们让蛇看一下一直吃的结果,
都吓坏了,到不能吃的时候,直接就不吃了,同时就是把吃掉导致的结果全部恢复没吃的状态;
这个问题也就是最长的蛇吃掉最短的蛇,同时保证自己不被吃,其他的根本不需要管,上一轮的操作会导致下一轮的回合怎么办,所以,最长的蛇预知未来,该
就吃谁,但同时,吃掉蛇后,序列会改变,但是我们要求仍是最长的蛇吃掉最小的蛇,所以我们要\(sort\)一下,然后就变成了一道光,直到用了multiset才回来。
思路考虑完了,该考虑怎么实现
当蛇的条数(\(n\))等于3的时候,那么此时就是20分的情况,特判一下
当蛇的条数(\(n\))大于等于3的时候,需要一种支持查询最大值,最小值,动态插入删除的数据结构(因为都在疯狂吃,谁管谁大谁小),用平衡树 multiset维护一下
我们去求第一个无法进行的回合,也就是最长的蛇把最小的蛇吃了以后,会被其他长度的蛇吃掉,的时候,这也就是答案
那么怎么去求解第一个无法进行的回合:
站在最长的蛇的角度想,只有它在吃掉最段蛇后的所有回合中 都会不被吃掉,操作才会进行。
这里的所有回合指该回合之后,直到第一个一定不会进行操作的回合。
考虑倒序枚举操作序列,标记所有蛇是否被吃掉,枚举到被删除蛇时判断即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#define int long long
using namespace std;
const int maxn=1e6+10;
struct Snake
{
int val, id;
bool operator < (const Snake &sec_) const {
if (val != sec_.val) return sec_.val > val;
return sec_.id > id;
}
};
int QwQ,n,opt[maxn],a[maxn];
//QwQ表示的是操作序列的迭代器
// rest[i]表示第i次操作能否成功
bool suc[maxn],del[maxn];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
multiset<Snake> s[11];
void init(int now)//掌管输入大权
{
QwQ=0;
memset(del,0,sizeof (del));
memset(suc,0,sizeof(suc));
if(now==1)
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
}
}
else
{
int k=read();
for(int i=1;i<=k;i++)
{
int s=read();
a[s]=read();
}
}
}
void solve(int now_, int lth_) {
Snake small = *s[now_].begin(), big = *s[now_].rbegin();
if (s[now_].size() == 3)
{
multiset <Snake>::iterator it = s[now_].begin();
++ it;
if (big.val - small.val < (*it).val || (big.val - small.val == (*it).val && big.id < (*it).id))
{
suc[lth_] = false;
} else {
suc[lth_] = suc[lth_ + 1] = true;
opt[++ QwQ] = small.id;
opt[++ QwQ] = (*it).id;
del[small.id] = del[(*it).id] = true;
}
return ;
}
s[now_].erase(s[now_].find(big));
s[now_].erase(s[now_].begin());
s[now_].insert((Snake) {big.val - small.val, big.id});
solve(now_, lth_ + 1);
if (!del[big.id]) {
suc[lth_] = true;
opt[++ QwQ] = small.id;
del[small.id] = true;
} else
{
for (int i = 1; i <= QwQ; ++ i) del[opt[i]] = false;
QwQ = 0;
}
}
signed main()
{
int t=read();
for(int now=1;now<=t;now++)
{
init(now);
for(int i=1;i<=n;i++)
{
s[now].insert((Snake) {a[i],i});
}
solve(now,0);
for(int i=0;i<=n;i++)
{
if(!suc[i])
{
printf("%d\n",n-i);
break;
}
}
}
return 0;
}