纪中DAY10做题小结

T1:库特的向量(code)

Description
从前在一个美好的校园里,有一只(棵)可爱的弯枝理树。她内敛而羞涩,一副弱气的样子让人一看就想好好疼爱她。仅仅在她身边,就有许多女孩子想和她BH,比如铃,库特,等等。不过,除却巫山不是云,理树的心理只有那个帅气高大的男孩子——恭介,这让女孩子们不得不终日唉声叹气,以泪洗面。不过恭介是那样强大而完美,根本没有办法击败他,她们也只好咬牙忍痛度日,以待反击之时。
终于,她们获得了一次机会。机智的库特利用弹道学、密码学、宇宙学的知识设计出了一个密室,可以让进入的人无法从内部打开出口。库特设计密码的过程很奇葩,是由两个用整数坐标表示的n 维向量导出的。神奇的是,对于这两个向量中的任意一个,无论如何将它的坐标打乱(例如(a1,a2,a3)变成(a3,a1,a2)),打乱后的数量积都不会比原来的两个向量的数量积小。而库特就把原来的两个向量的数量积作为了密码。现在她们只用把恭介引入就可以了。但是,好事多磨,由于她们的粗心大意,在测试密室的时候不小心把自己给关了进去,而且还带走了密码纸。在外面的铃只找到了库特写着两个打乱后的向量的草稿。哇呼~能不能解救这些萌妹子,就看你了。

Input
三行。第一行一个整数N,表示N 维。
第2~3 行每行N 个整数,表示打乱后的两个向量(a1,a2,a3,a4…an),(b1,b2,b3,b4…bn).

Output
如题目要求,输出库特设计的密码

Sample Input

3
1 3 -5
-2 4 1

Sample Output

-25

Data Constraint
对于50%的数据 n<=8 , |ai|,|bi|<=1000
对于100%的数据 n<=1000, |ai|,|bi|<=100000

简要思路:这道题是一道简单的贪心题,一组数据从小到大排序,另一组数据从大到小排序,两组数据对应位置上的数相乘,加上积就是结果。至于证明,感性理解吧
我们就证 n = 2 n = 2 n=2时的情况吧,设 a 1 = A a_1 = A a1=A b 1 = B b_1 = B b1=B,再设 a 2 &gt; a 1 a_2 &gt; a_1 a2>a1 b 2 &gt; b 1 b_2 &gt; b_1 b2>b1,则 a 2 a_2 a2可表示成 a 1 + R a_1 + R a1+R,即 A + R A + R A+R b 2 b_2 b2可表示成 b 1 + T b_1 + T b1+T,即 B + T B + T B+T,其中 B B B T T T均为正整数。
若按照上述贪心的方法,则得出的结果为 a n s 1 = A ∗ ( B + T ) + ( A + R ) ∗ B ans_1 = A * (B + T) + (A + R) * B ans1=A(B+T)+(A+R)B,得出 a n s 1 = 2 ∗ A ∗ B + A ∗ T + B ∗ R ans_1 = 2 * A * B + A * T + B * R ans1=2AB+AT+BR,若反其道而行之,则结果为 a n s 2 = A ∗ B + ( A + R ) ∗ ( B + T ) ans_2 = A * B + (A + R) * (B + T) ans2=AB+(A+R)(B+T),得出 a n s 2 = 2 ∗ A ∗ B + A ∗ T + B ∗ R + R ∗ T ans_2 = 2 * A * B + A * T + B * R + R * T ans2=2AB+AT+BR+RT,由定义知 R ∗ T R * T RT必为正整数,故 a n s 2 &gt; a n s 1 ans_2 &gt; ans_1 ans2>ans1,故在 n = 2 n = 2 n=2时结论成立。
想象一下我们用 s o r t sort sort排结构体时定义排序规则的方法,若将这一结论应用到两个数组的排序上(假设一个已排好,我们给另一个排序),可以发现,两个数组的大数会尽量离得远一点,这就非常感性的 证明了这个贪心的正确性。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1005;
int numa[N] , numb[N];
ll ans;
int n;
inline void read( int & res ) {
	res = 0;
	int pd = 1;
	char a = getchar();
	while ( a < '0' || a > '9' ) {
		if ( a == '-' ) {
			pd = -pd;
		}
		a = getchar();
	}
	while ( a >= '0' && a <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
		a = getchar();
	}
	res *= pd;
	return;
}
inline bool cmp( int x , int y ) {
	return x > y;
}
int main () {
	read(n);
	for ( int i = 1 ; i <= n ; ++i ) {
		read(numa[i]);
	}
	for ( int i = 1 ; i <= n ; ++i ) {
		read(numb[i]);
	}
	sort( numa + 1 , numa + 1 + n );
	sort( numb + 1 , numb + 1 + n , cmp );
	ans = 0;
	for ( int i = 1 ; i <= n ; ++i ) {
		ans += ( (ll)numa[i] * (ll)numb[i] );
	}
	printf("%lld",ans);
	return 0;
}

T2:恭介的法则(rule)

Description
终于,在众亲们的奋斗下,最终boss 恭介被关进了库特设计的密室。正当她们松了一口气时,这个世界却发生了天翻覆地的变化:地面开始下沉,天空开始变成血红色,海水沸腾……一幅世界末日的图景。美鱼从她手中的古籍《若山牧水诗歌集》中发现了原因:白鸟は かなしからずや 空の青 海のあをにも 染まずただよふ(更正为:白鸟は 哀しからずや /空の青 うみのあをにも /染まらずただよふ) 。大(xia)意(shuo)就是狡猾的恭介在创造这个世界的时候就篡改了法则。而这个法则的起源,就是一只生死之间的猫。这个猫被关在一个黑盒子里,盒子里有两个毒气罐,如果有任意一个毒气罐被打开那么猫将会被杀死,法则也能得到纠正。然而外界能控制的仅仅是这两个毒气罐被打开的概率。假设第一个毒气罐被打开的概率为1/x,第二个毒气罐为1/y(x,y 为正整数),那么当两个概率和为1/(n!)时,猫将会被莫名其妙地杀死。现在美鱼想知道,有多少对(x,y)可以让猫被莫名其妙杀死。

Input
一行,一个正整数n

Output
一行,满足题意的(x,y)对数。

Sample Input

6

Sample Output

135

Data Constraint
对于30%的数据 n<=6
对于60%的数据 n<=50
对于100%的数据 n<=700000

简要思路:这题涉及到一些数论知识以及压位高精不是我的 )。
首先,就是众望所归的推公式了,由题,不难得出 x + y x ∗ y = 1 n ! \frac{x + y}{x*y} = \frac{1}{n!} xyx+y=n!1
转换为 x ∗ n ! + y ∗ n ! = x ∗ y x * n! + y * n! = x * y xn!+yn!=xy
移项得 x = y ∗ n ! y − n ! x = \frac{y *n!}{y-n!} x=yn!yn!
由题意 x x x y y y均为正整数,不难看出 y y y一定要大于 n ! n! n!
y = n ! + k y = n! + k y=n!+k k ∈ Z + k \in Z^+ kZ+,不难得出: x = n ! 2 k + n ! x = \frac{n!^2}{k} + n! x=kn!2+n!,可得 k k k必为 n ! 2 n!^2 n!2的因数,且 x x x k k k一一对应,故 k k k ( x , y ) ( x , y ) (x,y)也一一对应,本题的答案实际上为 n ! 2 n!^2 n!2的因数个数。
由唯一分解定理,设 p i p_i pi为素数,若数 n n n可表示为 p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p m k m p_1^{k_1} * p_2^{k_2} * ... * p_m^{k_m} p1k1p2k2...pmkm,则因数个数为 ( k 1 + 1 ) ∗ ( k 2 + 1 ) ∗ . . . ∗ ( k m + 1 ) ( k_1 + 1 ) * ( k_2 + 1 ) * ... * ( k_m + 1 ) (k1+1)(k2+1)...(km+1)
最后,有一个大佬教会了我一个小技巧快速算出 n ! n! n!的质因数的指数:
先搬出一个结论: n ! n! n!分解质因数之后质数 p p p的指数为 ∑ c = 1 p c ≤ n ⌊ n p c ⌋ \sum_{c=1}^{p^c\le n} \lfloor\frac{n}{p^c} \rfloor c=1pcnpcn
证明: n ! n! n!的质因数就是 1... n 1...n 1...n的所有质因数乘起来,所以只要加起来这些数的质因数的指数就可以了;
并且, ⌊ n p c ⌋ \lfloor\frac{n}{p^c} \rfloor pcn表示小于等于n的数中质数 p p p的指数至少为 c c c的数的个数;每个数的贡献是它的指数,每次枚举 c c c贡献都会加一,那么枚举到准确的指数的时候贡献就刚好算完了。由此上述结论得证。
最后,弄一个压八位的高精度即可(小优化:当因数个数统计到一定大小时再乘进高精度算式)。

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define mod 100000000//压八位更好,十二位好像会炸
#define lim 2000000000
using namespace std;
const int N = 1000005;
ll ans[N];
int vis[N] , prime[N];
int n , ptot;
ll sum , tem;
inline void pre() {
	for ( int i = 2 ; i <= n ; ++i ) {
		if ( !vis[i] ) {
			prime[++ptot] = i;
		}
		for ( int j = 1 ; j <= ptot ; ++j ) {
			if ( i * prime[j] > n ) {
				break;
			}
			vis[i * prime[j]] = 1;
			if ( !( i % prime[j] ) ) {
				break;
			}
		}
	}
}
inline void multi( ll num ) {
	for( int i = 1 ; i <= ans[0] ; ++i ) {
		ans[i] *= num;
	}
	for ( int i = 1  ; i <= ans[0] ; ++i ) {
		ans[i + 1] += ans[i] / mod;
		ans[i] %= mod;
	}
	while ( ans[ans[0] + 1] > 0 ) {
		ans[0]++;
		ans[ans[0] + 1] += ans[ans[0]] / mod;
		ans[ans[0]] %= mod;
	}
	return;
}
int main () {
	scanf("%d",&n);
	ptot = 0;
	ans[1] = 1;
	ans[0] = 1;
	pre();
	tem = 1;
	for ( int i = 1 ; i <= ptot ; ++i ) {
		sum = 0;
		ll now = n;
		while ( now >= prime[i] ) {
			now /= prime[i];//小技巧 
			sum += now;
		}
		sum = sum * 2 + 1;
		if ( sum * tem > lim ) {
			multi(tem);//小优化
			tem = 1;
		}
		tem *= sum;
	}
	if ( tem > 1 ) {
		multi(tem);
	}
	printf("%lld",ans[ans[0]]);
	for ( int i = ans[0] - 1 ; i >= 1 ; --i ) {
		printf("%08lld",ans[i]);
	}
	return 0;
}

T3:沙耶的玩偶(doll)

Description
在美鱼和理树后援团拯救世界的同时,外表柔弱的理树也开始坚强起来,思考着离开这个世界的办法。误打误撞地,她遇上了正在教室破坏课桌打开迷宫入口的沙耶。沙耶告诉理树,这个世界的出口就是这个迷宫的出口。于是理树毫不犹豫地跟沙耶一起跳进了迷宫。在迷宫里,两个女孩子互帮互助,一会儿割绳子,一会儿泡温泉,一会儿雕冰块,跌跌撞撞地走到了终点。不出所料,终点也有一个机关在等着她们。
终点的机关是一个立着的mn 的方格棋盘,在有些格子上放了一个玩偶,而有些地方直接挖了个大坑。只有取走所有玩偶才能打开出口。但是,由于奇怪的设定,理树和沙耶不能直接触碰玩偶,他们需要操纵机器人来收集它。机器人的走法很奇怪,和国际象棋的马有点像,只不过马可以走任意方向的12 路线,它们只会由上往下走rc(或cr)的路线,不能回头。而机器人一旦经过一个有玩偶的格子,那个格子上的玩偶将被回收,并且在机器人离开时,那个格子会变成一个坑。理树可以把机器人放在任何一个有玩偶的格子上作为起点,也可以在任何一个有玩偶的格子回收机器人。机器人行走可以视为瞬移,只不过每一次设置新起点都会消耗1 时间。并且,有坑的格子不能落脚。
就在这个紧要关头,玩偶狂热爱好者的沙耶却流着口水智商归0。理树不得不转而求助你,帮忙计算出最少多少时间就能收集到所有玩偶。

Input
第一行包含4 个整数M、N、R、C,意义见问题描述。接下来M 行每行一个长度为N 的字符串。如果某个字符是’.’,表示这个地方有一个玩偶;如果这个字符是’x’,表示这个地方是坑。

Output
输出一个整数,表示最短时间。

Sample Input

3 3 1 2
...
.x.
...

Sample Output

4

Data Constraint
30%的数据中,1<=M,N<=4,1<=R,C<=3。
70%的数据中,1<=M<=20,1<=N<=4,1<=R,C<=3。
100%的数据中,1<=M,N<=50,1<=R,C<=10。

Hint
图1

简要思路:这题正解涉及到了二分图最大匹配,不过,考虑到它的数据范围不是很大,迭代加深搜再加上适当的剪枝可以拿大概30%的分数。我在考场上很快想到根据题目的数据构造信息竞赛标准意义上的图,也想到了什么最小覆盖,最大独立集,然而,却不会二分图匹配险些爆零
好了,言归正传,关于这题,我们很容易看出当机器人从一个点跳出去后就回不来了,因而,通过有向边连接方格形成的图是不存在环的,一个图只要不存在奇环,就是二分图,通过样例,我们可以画出下面的图(按遍历顺序编号)嫌图丑的请轻喷
图1
根据对图片的观察,我们发现,题目要我们求的是图的最大独立集,也就是最小点覆盖(证明?不存在的 ),图上的点尽可能地找到一个儿子并与之匹配(自己本身还可以被其他的父亲匹配),每成功匹配一个点对机器就少运行一次,本题的图已证明是二分图,用二分图最大匹配算法求出最大匹配,总点数减去最大匹配数就是本题答案。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n , m , r , c , sum , bcnt , pcnt;
int xx[5] , yy[5] , room[55][55] , num[55][55] , head[2505] , match[2505] , vis[2505];
char ss[55];
struct node{
	int next;
	int to;
}str[10005];
inline void read( int & res ) {
	res = 0;
	int pd = 1;
	char a = getchar();
	while ( a < '0' || a > '9' ) {
		if ( a == '-' ) {
			pd = -pd;
		}
		a = getchar();
	}
	while ( a >= '0' && a <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
		a = getchar();
	}
	res *= pd;
	return;
}
inline void insert( int from , int to ) {
	str[++bcnt].next = head[from];
	head[from] = bcnt;
	str[bcnt].to = to;
	return;
}
inline bool dfs( int cur ) {
	for ( int i = head[cur] ; i ; i = str[i].next ) {
		int sn = str[i].to;
		if ( !vis[sn] ) {
			vis[sn] = 1;
			if ( !match[sn] || dfs( match[sn] ) ) {
				match[sn] = cur;
				return true;
			}
		}
	}
	return false;
}
int main () {
	read(n);
	read(m);
	read(r);
	read(c);
	bcnt = 0;
	pcnt = 0;
	sum = 0;
	xx[1] = r;
	yy[1] = c;
	xx[2] = c;
	yy[2] = r;
	xx[3] = r;
	yy[3] = -c;
	xx[4] = c;
	yy[4] = -r;
	for ( int i = 1 ; i <= n ; ++i ) {
		scanf("%s",ss);
		for ( int j = 1 ; j <= m ; ++j ) {
			if ( ss[j - 1] == 'x' ) {
				room[i][j] = 1;
			} else {
				num[i][j] = ++pcnt;
			}
		}
	}
	for ( int i = 1 ; i <= n ; ++i ) {
		for ( int j = 1 ; j <= m ; ++j ) {
			if ( !room[i][j] ) {
				for ( int k = 1 ; k <= 4 ; ++k ) {
					int x = xx[k] + i;
					int y = yy[k] + j;
					if ( x >= 1 && x <= n && y >= 1 && y <= m && !room[x][y] ) {
						insert( num[i][j] , num[x][y] );
					}
				}
			}
		}
	}
	sum = 0;
	for ( int i = 1 ; i <= pcnt ; ++i ) {
		memset( vis , 0 , sizeof(vis) );
		if ( dfs(i) ) {
			sum++;
		}
	}
	printf("%d",pcnt - sum);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值