纪中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
>
a
1
a_2 > a_1
a2>a1,
b
2
>
b
1
b_2 > 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=2∗A∗B+A∗T+B∗R,若反其道而行之,则结果为
a
n
s
2
=
A
∗
B
+
(
A
+
R
)
∗
(
B
+
T
)
ans_2 = A * B + (A + R) * (B + T)
ans2=A∗B+(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=2∗A∗B+A∗T+B∗R+R∗T,由定义知
R
∗
T
R * T
R∗T必为正整数,故
a
n
s
2
>
a
n
s
1
ans_2 > 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!}
x∗yx+y=n!1
转换为
x
∗
n
!
+
y
∗
n
!
=
x
∗
y
x * n! + y * n! = x * y
x∗n!+y∗n!=x∗y
移项得
x
=
y
∗
n
!
y
−
n
!
x = \frac{y *n!}{y-n!}
x=y−n!y∗n!
由题意
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^+
k∈Z+,不难得出:
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}
p1k1∗p2k2∗...∗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=1∑pc≤n⌊pcn⌋
证明:
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
简要思路:这题正解涉及到了二分图最大匹配,不过,考虑到它的数据范围不是很大,迭代加深搜再加上适当的剪枝可以拿大概30%的分数。我在考场上很快想到根据题目的数据构造信息竞赛标准意义上的图,也想到了什么最小覆盖,最大独立集,然而,却不会二分图匹配险些爆零 。
好了,言归正传,关于这题,我们很容易看出当机器人从一个点跳出去后就回不来了,因而,通过有向边连接方格形成的图是不存在环的,一个图只要不存在奇环,就是二分图,通过样例,我们可以画出下面的图(按遍历顺序编号)嫌图丑的请轻喷 :
根据对图片的观察,我们发现,题目要我们求的是图的最大独立集,也就是最小点覆盖(证明?不存在的 ),图上的点尽可能地找到一个儿子并与之匹配(自己本身还可以被其他的父亲匹配),每成功匹配一个点对机器就少运行一次,本题的图已证明是二分图,用二分图最大匹配算法求出最大匹配,总点数减去最大匹配数就是本题答案。
#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;
}