20190708三人开黑CF模拟赛

时间:2023-03-09 20:14:40
20190708三人开黑CF模拟赛

7月8号晚上8点和两位巨佬开了一场虚拟cf: [Helvetic Coding Contest 2018 online mirror (teams allowed, unrated)]

我这么蔡,只AC了A2、C1、C2、E1(被巨佬吊打)

20190708三人开黑CF模拟赛

我就说一下我写的几道题吧:

A2. Death Stars (medium)

The stardate is 1983, and Princess Heidi is getting better at detecting the Death Stars. This time, two Rebel spies have yet again given Heidi two maps with the possible locations of the Death Star. Since she got rid of all double agents last time, she knows that both maps are correct, and indeed show the map of the solar system that contains the Death Star. However, this time the Empire has hidden the Death Star very well, and Heidi needs to find a place that appears on both maps in order to detect the Death Star.

The first map is an N × M grid, each cell of which shows some type of cosmic object that is present in the corresponding quadrant of space. The second map is an M × N grid. Heidi needs to align those two maps in such a way that they overlap over some M × M section in which all cosmic objects are identical. Help Heidi by identifying where such an M × M section lies within both maps.

Input

The first line of the input contains two space-separated integers N and M (1 ≤ N ≤ 2000, 1 ≤ M ≤ 200, M ≤ N). The next N lines each contain M lower-case Latin characters (a-z), denoting the first map. Different characters correspond to different cosmic object types. The next M lines each contain N characters, describing the second map in the same format.

Output

The only line of the output should contain two space-separated integers i and j, denoting that the section of size M × M in the first map that starts at the i-th row is equal to the section of the second map that starts at the j-th column. Rows and columns are numbered starting from 1.

If there are several possible ways to align the maps, Heidi will be satisfied with any of those. It is guaranteed that a solution exists.

翻译后的人话:

给你两个矩阵,前一个\(N×M\),后一个\(M×N\),然后再两个矩阵中识别一个\(M×M\)的矩阵,这个矩阵都存在于两个矩阵中

好水啊,可以用kmp,不过我太蔡不会于是直接hash暴力匹配

这里用了单哈希,参考了巨佬队友tanao_的代码,我双哈希不知道为什么T飞(可能是我太蔡了)

直接上代码(把注释去掉就变成双hash了):

#include <cstdio>
#include <cctype>
#include <iostream>
#define reg register
#define int long long
using namespace std;
const int MaxN=2001;
const int MaxM=201;
const int inf=0x7ffffffffffffff; // 14 f
const int base1=131;
const int base2=13131;
const int p1=4503599627370001ll;
const int p2=4503599627391001ll;
int n,m;
string s1[MaxN],s2[MaxN];
int s1hash1[MaxN],s2hash1[MaxM][MaxN];
int s1hash2[MaxN],s2hash2[MaxM][MaxN];
template <class t> inline void rd(t &s)
{
s=0;
reg char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c))
s=(s<<3)+(s<<1)+(c^48),c=getchar();
return;
}
inline int gethash1(string s)
{
reg int res=0;
for(int i=0;i<s.length();++i)
res=(res*base1+s[i])%p1;
return res;
}
inline int gethash2(string s)
{
reg int res=0;
for(int i=0;i<s.length();++i)
res=(res*base2+s[i])%p2;
return res;
}
inline bool ck(int x,int y)
{
for(int i=1;i<=m;++i)
// if(s1hash1[x+i-1]!=s2hash1[i][y]||s1hash2[x+i-1]!=s2hash2[i][y])
if(s1hash1[x+i-1]!=s2hash1[i][y])
return false;
return true;
}
signed main(void)
{
rd(n);rd(m);
for(int i=1;i<=n;++i)
cin>>s1[i];
for(int i=1;i<=m;++i)
cin>>s2[i];
for(int i=1;i<=n;++i)
{
s1hash1[i]=gethash1(s1[i]);
// s1hash2[i]=gethash2(s1[i]);
}
for(int i=1;i<=m;++i)
for(int j=1;j<=n-m+1;++j)
{
s2hash1[i][j]=gethash1(s2[i].substr(j-1,m));
// s2hash2[i][j]=gethash2(s2[i].substr(j-1,m));
}
for(int i=1;i<=n-m+1;++i)
for(int j=1;j<=n-m+1;++j)
if(ck(i,j))
return printf("%lld %lld",i,j),0;
return 0;
}

C1. Encryption (easy)

Rebel spy Heidi has just obtained the plans for the Death Star from the Empire and, now on her way to safety, she is trying to break the encryption of the plans (of course they are encrypted – the Empire may be evil, but it is not stupid!). The encryption has several levels of security, and here is how the first one looks.

Heidi is presented with a screen that shows her a sequence of integers A and a positive integer p. She knows that the encryption code is a single number S, which is defined as follows:

Define the score of X to be the sum of the elements of X modulo p.

Heidi is given a sequence A that consists of N integers, and also given an integer p. She needs to split A into 2 parts such that:

  • Each part contains at least 1 element of A, and each part consists of contiguous elements of A.
  • The two parts do not overlap.
  • The total sum S of the scores of those two parts is maximized. This is the encryption code.

Output the sum S, which is the encryption code.

Input

The first line of the input contains two space-separated integer N and p (2 ≤ N ≤ 100 000, 2 ≤ p ≤ 10 000) – the number of elements in A, and the modulo for computing scores, respectively.

The second line contains N space-separated integers which are the elements of A. Each integer is from the interval [1, 1 000 000].

Output

Output the number S as described in the problem statement.

样例1

输入

4 10
3 4 7 2

输出

16

经过印度女工翻译后的人话:

把长度为n的序列分成两个部分,使每个子序列的和对p取模后的结果的和最大

样例解释1:

\((3+4) mod 10+(7+2) mod 10 = 16\)

\(n<=100000\) 直接前缀和暴力枚举断点水过

Code:

#include <cstdio>
#include <cctype>
#include <algorithm>
#define int long long
#define reg register
using namespace std;
const int MaxN=100001;
template <class t> inline void rd(t &s)
{
s=0;
reg char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c))
s=(s<<3)+(s<<1)+(c^48),c=getchar();
return;
}
int a[MaxN],f[MaxN];
inline int getsum(int l,int r)
{
return f[r]-f[l-1];
}
signed main(void)
{
int n,p;
reg int ans=0;
rd(n);rd(p);
for(int i=1;i<=n;++i)
rd(a[i]),f[i]=f[i-1]+a[i];
for(int i=1;i<n;++i)
ans=max(ans,(getsum(1,i)%p)+(getsum(i+1,n)%p));
printf("%lld",ans);
return 0;
}

C2. Encryption (medium)

Heidi has now broken the first level of encryption of the Death Star plans, and is staring at the screen presenting her with the description of the next code she has to enter. It looks surprisingly similar to the first one – seems like the Empire engineers were quite lazy...

Heidi is once again given a sequence A, but now she is also given two integers k and p. She needs to find out what the encryption key S is.

Let X be a sequence of integers, and p a positive integer. We define the score of X to be the sum of the elements of X modulo p.

Heidi is given a sequence A that consists of N integers, and also given integers k and p. Her goal is to split A into k part such that:

  • Each part contains at least 1 element of A, and each part consists of contiguous elements of A.
  • No two parts overlap.
  • The total sum S of the scores of those parts is maximized.

Output the sum S – the encryption code.

Input

The first line of the input contains three space-separated integer N, k and p (k ≤ N ≤ 20 000, 2 ≤ k ≤ 50, 2 ≤ p ≤ 100) – the number of elements in A, the number of parts A should be split into, and the modulo for computing scores, respectively.

The second line contains N space-separated integers that are the elements of A. Each integer is from the interval [1, 1 000 000].

Output

Output the number S as described in the problem statement.

上一题的升级版

人话:把长度为n的序列分成k个部分,使每个子序列的和对p取模后的结果的和最大

很明显,考虑dp

\(f[i][j]\) 表示前i个数分成j段时的最大结果

转移方程很简单,

\(f[i][j]=max(f[i][j],f[k][j-1]+(g[i]-g[k])mod p)\) \((0<=k<i)\)

解释一下,g[i]是前缀和,\(f[k][j-1]\)表示在第k个数断j-1个点时的最大结果,\((g[i]-g[k])\)就是k+1~i这段序列和,k是用来枚举用的,看不懂建议先去做一些基础的dp

分析一下复杂度\(O(n^2k)\),最大计算次数为\(2.0×10^{10}\),20亿,好,信心满满地相信CF的机子交上去

考虑优化,我们是不是可以把一个n搞成p来减少时间复杂度呢

\(f[i][j]\)表示前i个数对p取模后为i,分成j段时的最大结果

显而易见,首先,因为dp数组的第一维的范围只有\([0,p-1]\),那么\(f[i][j]\)只有可能从\(f[k][j-1]\)\((0<=k<p)\)转移过来,那么对应断点所得新的子序列的和就是\((g[i]-k)modp\),所以可以得到状态转移方程

\(f[i][j]=max(f[i][j],f[k][j-1]+(g[i]-k)modp)\)\(0<=k<p\)

于是,Code就出来了:

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#define reg register
#define int long long
using namespace std;
const int MaxN=20001;
const int MaxK=51;
template <class t> inline void rd(t &s)
{
s=0;
reg char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c))
s=(s<<3)+(s<<1)+(c^48),c=getchar();
return;
}
int n,k,p;
int a[MaxN],g[MaxN];
int f[MaxN][MaxK];
signed main(void)
{
memset(f,0x80,sizeof f);f[0][0]=0;
rd(n);rd(k);rd(p);
for(int i=1;i<=n;++i)
rd(a[i]),g[i]=g[i-1]+a[i],g[i]%=p;
for(int i=1;i<=n;++i)
for(int j=k;j;--j)
for(int k=0;k<p;++k)
f[g[i]][j]=max(f[g[i]][j],f[k][j-1]+(g[i]+p-k)%p);
printf("%lld",f[g[n]][k]);
return 0;
}

E1. Guard Duty (easy)

The Rebel fleet is afraid that the Empire might want to strike back again. Princess Heidi needs to know if it is possible to assign R Rebel spaceships to guard B bases so that every base has exactly one guardian and each spaceship has exactly one assigned base (in other words, the assignment is a perfect matching). Since she knows how reckless her pilots are, she wants to be sure that any two (straight) paths – from a base to its assigned spaceship – do not intersect in the galaxy plane (that is, in 2D), and so there is no risk of collision.

Input

The first line contains two space-separated integers R, B(1 ≤ R, B ≤ 10). For 1 ≤ i ≤ R, the i + 1-th line contains two space-separated integers xi and yi (|xi|, |yi| ≤ 10000) denoting the coordinates of the i-th Rebel spaceship. The following B lines have the same format, denoting the position of bases. It is guaranteed that no two points coincide and that no three points are on the same line.

Output

If it is possible to connect Rebel spaceships and bases so as satisfy the constraint, output Yes, otherwise output No (without quote).

使用谷歌翻译然后看看样例再大胆的猜想一下就看懂题意了,其实我当时也不知道我能一次A

还好我当时猜对了题意

有R个叛军基地和B个守卫基地,要求一种匹配,使每一个叛军基地都和一个守卫基地连线,如果存在一种连线方法能让每一个基地都有匹配并且这些线段两两不相交,那么输出Yes,否则输出No

\(1<=R,B<=10\),直接暴力枚举每一种情况,判断;如果\(R!=B\),直接输出No

暴力好写,但是怎么判断两条线段是否相交呢?

这里使用了快速排斥实验跨立实验法,参考一位巨佬的博客(好几个月前看过,好不容易翻出来的)

快速排斥实验:判断两条线段在 x 以及 y 坐标的投影是否有重合

人话:若A线段中x较大的端点比B线段中x较小的端点的x要小,

​ 或A线段中y较大的端点比B线段中y较小的端点的y要小,则AB线段不相交

20190708三人开黑CF模拟赛

如图,图(1)和图(2)可以自己推出符合快速排斥,可图(3)就不符合快速排斥了

那么就要用到跨立实验法,也就是矢量叉积:

设矢量 \(P = (x1, y1),Q = ( x2, y2 )\),则矢量叉积定义为:\(P×Q = x1×y2 - x2×y1\)

有$P×Q = - ( Q×P ) $和 \(P×( - Q ) = - ( P×Q )\)

若 \(P×Q > 0\) , 则 \(P\) 在 \(Q\) 的顺时针方向。

若 \(P×Q < 0\) , 则 \(P\) 在 \(Q\) 的逆时针方向。

若 \(P×Q = 0\) , 则 \(P\) 与 \(Q\) 共线,但可能同向也可能反向。

(1):20190708三人开黑CF模拟赛

如果两线段相交那么就意味着它们互相跨立,即如上图点 A 和 B 分别在线段 CD 两侧,点 C 和 D 分别在线 AB 两侧。

判断 A 点与 B 点是否在线段 DC 的两侧,即向量 A-D 与向量 B-D 分别在向量 C-D 的两端,也就是其叉积是异号的,即 。

同时也要证明 C 点与 D 点在线段 AB 的两端,两个同时满足,则表示线段相交。

(2):临界情况,也就是上式恰好等于 0 的情况下:

20190708三人开黑CF模拟赛

当出现如上图所示的情况的时候,显然,这种情况是相交的。只要将等号直接补上即可。

(3)再看看上面我给的图(3):

当出现如上所示的情况的时候,叉积都为 0, 可以通过跨立实验,但是两个线段并没有交点。不过还好,这种情况在第一步快速排斥已经被排除了。

给出AC代码:

#include <cstdio>
#include <cctype>
#include <iostream>
#include <cstdlib>
#define reg register
using namespace std;
const int MaxN=11;
struct Node
{
int x,y;
}a[MaxN],b[MaxN];
struct Line
{
int xa,xb,ya,yb;
}E[10001];
int en;
template <class t> inline void rd(t &s)
{
s=0;
reg char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c))
s=(s<<3)+(s<<1)+(c^48),c=getchar();
return;
}
inline bool tix(Line x,Line y)
{
if ((x.xa > x.xb ? x.xa : x.xb) < (y.xa < y.xb ? y.xa : y.xb) ||
(x.ya > x.yb ? x.ya : x.yb) < (y.ya < y.yb ? y.ya : y.yb) ||
(y.xa > y.xb ? y.xa : y.xb) < (x.xa < x.xb ? x.xa : x.xb) ||
(y.ya > y.yb ? y.ya : y.yb) < (x.ya < x.yb ? x.ya : x.yb))
return false;
if ((((x.xa - y.xa)*(y.yb - y.ya) - (x.ya - y.ya)*(y.xb - y.xa))*
((x.xb - y.xa)*(y.yb - y.ya) - (x.yb - y.ya)*(y.xb - y.xa))) > 0 ||
(((y.xa - x.xa)*(x.yb - x.ya) - (y.ya - x.ya)*(x.xb - x.xa))*
((y.xb - x.xa)*(x.yb - x.ya) - (y.yb - x.ya)*(x.xb - x.xa))) > 0)
return false;
return true;
}
int R,B;
bool vis[MaxN];
inline bool ck()
{
for(int i=1;i<=R;++i)
for(int j=i+1;j<=B;++j)
if(tix(E[i],E[j]))
return false;
return true;
}
inline void dfs(int dep)
{
if(dep>R)
{
if(ck())
{
puts("Yes");
exit(0);
}
return;
}
for(int i=1;i<=B;++i)
if(!vis[i])
{
vis[i]=true;
E[dep].xa=a[dep].x;
E[dep].ya=a[dep].y;
E[dep].xb=b[i].x;
E[dep].yb=b[i].y;
dfs(dep+1);
vis[i]=false;
}
return;
}
signed main(void)
{
rd(R);rd(B);
if(R!=B)
return puts("No"),0;
for(int i=1;i<=R;++i)
{
rd(a[i].x);rd(a[i].y);
}
for(int i=1;i<=B;++i)
{
rd(b[i].x);rd(b[i].y);
}
dfs(1);
puts("No");
return 0;
}

第一次写cf的解题报告awa