2020.01.01日常总结

洛谷P2856

【题目翻译】: 奶牛的名字都是由英文字母表的前 L ( 1 ≤ L ≤ 26 ) L(1 \leq L \leq 26) L(1L26)个字母构成的(全部为大写字母且长度 ≤ 10 \leq 10 10)。现在奶牛们想设计一种手机, 有 B B B个按键。请你把这 L L L个字母按顺序分配给 B B B个按键,使能够通过按这些键而唯一确定的奶牛数尽量多。求最多可以唯一确定多少头奶牛,输出方案。如果有多种方案,输出编号小的按键容纳字母最多的方案。

【思路】: 暴力枚举每个字母分配给哪个按键。记 c h [ i ] ch[i] ch[i]表示字母 i i i分配给按键 c h [ i ] ch[i] ch[i]。不难发现, c h ch ch可以用搜索算法得出。

假设我们已经得到了 c h [ i ] ch[i] ch[i],考虑如何计算可以唯一确定多少头奶牛的名字。

依题意,每头奶牛的名字长度 ≤ 10 \leq 10 10,我们可以视整个字符串为一个 B + 1 B+1 B+1进制的数,这样就可以判断每个字符串的hash值。得到hash值后,我们就可以判断每个字符串是否只出现一次了。

【注意】: 数据的开头有一个没有用的整数,必须把它输入但忽略掉。

【代码】:

int L,B,ans,ch[30],CH[30];
string str[1010];int n;
long long num[1010];
void updata_answer(){
	memset(num,0,sizeof(num));
	for(int i=1;i<=n;i++)
		for(int j=0;j<str[i].size();j++)
			num[i]=num[i]*(B+1)+ch[str[i][j]-'A'+1];//计算hash值
	sort(num+1,num+n+1);
	register int tot=0;
	for(int i=1;i<=n;i++)
		if (num[i]!=num[i-1]&&num[i]!=num[i+1]) tot++;//计算有多少个不同的字符串
	if (tot>=ans){//注意这样才保证了答案字典序最小
		ans=tot;
		memcpy(CH,ch,sizeof(ch));
	}
}
void dfs(int sub,int color){
	if (sub>L){
		if (color>=B)
			updata_answer();
		return;
	}
	if (sub>1&&color<B){
		ch[sub]=color+1;
		dfs(sub+1,color+1);
	}
	if (color+L-sub>=B){
		ch[sub]=color;
		dfs(sub+1,color);
	}
	ch[sub]=-1;
}
int test_number;
int main(){
	cin>>test_number;test_number=1;//忽略掉这个无用的数字
	while (test_number--){
		memset(ch,-1,sizeof(ch));
		cin>>B>>L>>n;
		for(int i=1;i<=n;i++)
			cin>>str[i];
		dfs(1,1);CH[0]=CH[1]-1;
		printf("%d",ans);
		for(int i=1;i<=L;i++)
			if (CH[i]!=CH[i-1])
				printf("\n%c",(char)(i+'A'-1));
			else printf("%c",(char)(i+'A'-1));
		printf("\n");
	}
	return 0;
}

洛谷P3708

【题意】: 输入一个整数 n n n,记
F ( i ) = ∑ j = 1 n i % j F(i)=\sum^n_{j=1} i \%j F(i)=j=1ni%j
F ( 1 ) , F ( 2 ) , F ( 3 ) . . . F ( n ) F(1),F(2),F(3)...F(n) F(1),F(2),F(3)...F(n)

【思路】: 假设我们已经知道了 F ( i ) F(i) F(i),考虑如何 O ( 1 ) O(1) O(1)推得 F ( i + 1 ) F(i+1) F(i+1),我们发现被模数增加 1 1 1,取模后的结果要么变为 0 0 0,要么也随之增加 1 1 1

假设所有的结果都增加 1 1 1,那么结果就增加 n n n。哪些数会变为零呢?当然是 i + 1 i+1 i+1的因数啦。

举个例子, n = 5 , F ( 4 ) = 4 % 1 + 4 % 2 + 4 % 3 + 4 % 4 + 4 % 5 = 0 + 0 + 1 + 0 + 4 , F ( 5 ) = 5 % 1 + 5 % 2 + 5 % 3 + 5 % 4 + 5 % 5 = 0 + 1 + 2 + 1 + 0 n=5,F(4)=4 \% 1+4 \% 2+4 \% 3+4 \%4+4 \%5=0+0+1+0+4,F(5)=5 \% 1 + 5 \% 2+5 \% 3 +5 \% 4 +5 \% 5=0+1+2+1+0 n=5,F(4)=4%1+4%2+4%3+4%4+4%5=0+0+1+0+4,F(5)=5%1+5%2+5%3+5%4+5%5=0+1+2+1+0。对比 F ( 4 ) , F ( 5 ) F(4),F(5) F(4),F(5)中的每一项就可以得到如上的规律了。

Q ( i ) Q(i) Q(i)表示 i i i的因数和,则:
F ( i + 1 ) = F ( i ) + n − Q ( i + 1 ) F(i+1)=F(i)+n-Q(i+1) F(i+1)=F(i)+nQ(i+1)

这就可以递推计算了,当然 F ( 1 ) = n − 1 F(1)=n-1 F(1)=n1,而 Q ( i ) Q(i) Q(i)可以在 O ( n × l o g n ) O(n \times log_n) O(n×logn)的时间复杂度内提前计算出来,然后就是 O ( n ) O(n) O(n)递推计算了。

【代码】:

#define ll long long
void write(ll a,bool b){
	if (a==0){
		if (b) putchar('0');
		return;
	}
	else if (a<0){
		putchar('-');
		write(-a,false);
	}
	else{
		write(a/10,false);
		putchar(a%10+'0');
	}
}
void print(ll a){
	write(a,true);
	putchar(' ');
}
const int N=1e6+100;
ll f[N],cnt[N];int n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n/i;j++)
			f[i*j]+=i;
	cnt[1]=n-1;
	for(int i=2;i<=n;i++)
		cnt[i]=cnt[i-1]+n-f[i];
	for(int i=1;i<=n;i++)
		print(cnt[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值