• 2020


    今年的题着实令人着迷,(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
    ",-y);
    	    else printf("%d
    ",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
    ", d, m, -y);
          } else {
            printf("%lld %lld %lld
    ", 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
    ", 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-i);
    				break;
    			}
    		}
    	}
    	return 0;
    }
    

    100分做法:(ちょっと待って)

  • 相关阅读:
    表格标签
    图片标签
    超链接标签
    媒体标签
    实体标签
    html常用的标签
    头信息的作用
    【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
    【bzoj3309】DZY Loves Math 莫比乌斯反演+线性筛
    【bzoj4010】[HNOI2015]菜肴制作 拓扑排序+堆
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/13995587.html
Copyright © 2020-2023  润新知