[USACO2.2]序言页码 Preface Numbering

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/84671438

链接

洛谷
USACO


大意

1 n 1\sim n 所有罗马数字各个字母的出现次数。
n 3500 n\leq 3500


思路

直接暴力,不过这题恶心的地方在于可能有这样的 I V , I X , X L IV,IX,XL 等带减号的,我们也把它们编入字典,然后特判即可

时间复杂度: O ( n l o g 10 n ) O(nlog_{10}n)

洛谷上有位 d a l a o dalao 用数位 d p dp 做的,本蒟蒻太菜不会,各位大佬可以自行借鉴。


代码

/*
ID:hzbismy1
LANG:C++
TASK:preface
*/
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;int b[7],n;//b表示对应字母的出现次数
const int a[14]={4000,1000,900,500,400,100,90,50,40,10,9,5,4,1};//表示字典
const char c[7]={'M','D','C','L','X','V','I'};//表示字母
inline char Getchar()
{
    static char buf[100000],*p1=buf+100000,*pend=buf+100000;
    if(p1==pend)
	{
        p1=buf; pend=buf+fread(buf,1,100000,stdin);
        if (pend==p1) return -1;
    }
    return *p1++;
}
inline int read()
{
	char c;int d=1,f=0;
	while(c=Getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=Getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void write(register long long x)
{
	if(x<0)write(45),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+48);
	return;
}//以上为输入输出优化
inline void add(register int x)
{
	int k;
	while(x>0)
	{
		for(register int i=1;i<=13;i++)
		if(x<a[i-1]&&x>=a[i]) 
		{
			x-=a[i];
			b[(i-1)>>1]++;//把所有数看做两个一组
			if(!(i&1)) b[(i>>1)+((i>>1)&1)]++;//找规律推出来的,本人是这样推的:
			/*我们发现所有带减号的符号的序号都是偶数的,所以有!(i&1)
			  然后,发现序号和相减的编号是这样的
			  2 2 4 4 6  6
			  2 4 6 8 10 12			  
			  发现下方是一个等差数列,我们把它除以2
			  2 2 4 4 6 6
			  1 2 3 4 5 6
			  发现偶数项是相等的,奇数项加了1,就有了(i>>1)+(i>>1)&1
			  因为本人是一个蒟蒻,不太懂位运算的优先级,所以只好乖乖的加了括号
			*/
		}
	}
	return;
}
signed main()
{
	file(preface);
	n=read();
	for(register int i=1;i<=n;i++) add(i);//预处理
	for(register int i=6;i>=0;i--)
	if(b[i]) putchar(c[i]),putchar(32),write(b[i]),putchar(10);//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/84671438
今日推荐