[2021.8纪中集训Day14]
@
1312. 老曹的忧郁
题目
Description
我们的主角——老曹,现在十分忧郁。原因是他在玩一个困难的游戏。
游戏规则如下:
有(N+1)*n/2个圆盘,在平面上摆成了一个变长为N个圆盘的等边三角形。每个圆盘上标有一个小写字母(‘a’_’z’),如下图所示。
对于每个字母,当我们可以由标为这个字母的圆盘为顶点,构成等边三角形时,我们称这个字母为不和谐的。例如途中的a和c。
现在给你N和这个等边三角形上的字母,要求哪些字母是不和谐的。Input
第一行:一个整数,N(1<=N<=12)
第二行:(N+1)*N/2个小写字母Output
一行:按字典序输出所有不和谐的字母,如果没有不和谐的字母,则输出“Harmonious”。
Sample Input
样例输入1: 3 aaaaaa 样例输入2: 3 abcdef
Sample Output
样例输出1: a 样例输出2: Harmonious
注意,如果输出多个字母,中间没有空格,比如那张图对应的答案就是ac
,题目差评😠
思路
一道水题,建个系,搜索一下,判下等边三角形就好了:
纵坐标\横坐标 | -3 | -2 | 1 | 0 | 1 | 2 | 3 |
---|---|---|---|---|---|---|---|
0 | a | ||||||
\(-\sqrt3\) | b | c | |||||
\(-2\sqrt 3\) | c | d | d | ||||
\(-3\sqrt 3\) | a | d | c | a |
比较难看
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const double eps = 1e-7;
const double sqrt3 = 1.73205081;
struct POINT {
double x , y;
};
POINT make(double x , double y) {
POINT tmp;
tmp.x = x , tmp.y = y;
return tmp;
}
double GetDist(POINT a , POINT b) {
return sqrt(
(a.x - b.x) * (a.x - b.x) +
(a.y - b.y) * (a.y - b.y)
);
}
inline bool fequal(double a , double b) {
return fabs(a - b) < eps;
}
bool CheckTriangle(POINT a , POINT b , POINT c) {
return fequal(GetDist(a , b) , GetDist(a , c)) && fequal(GetDist(a , c) , GetDist(b , c));
}
int n;
vector <POINT> a[30];
bool result;
POINT rec[5];
void dfs(int id , int pos , int rest) {
if(result)
return;
if(rest == 0) {
if(CheckTriangle(rec[1] , rec[2] , rec[3]))
result = true;
return;
}
if(pos == a[id].size())
return ;
dfs(id , pos + 1 , rest);
rec[rest] = a[id][pos];
dfs(id , pos + 1 , rest - 1);
}
int main() {
cin >> n;
for(int i = 0 , cnt = 0 ; i < n ; i++) {
for(int j = -i ; j <= i ; j += 2) {
char c;
cin >> c;
a[c - 'a'].push_back(make(sqrt3 * (double)cnt , j));
}
--cnt;
}
bool flag = false;
for(int i = 0 ; i < 26 ; i++) {
result = false;
dfs(i , 0 , 3);
flag |= result;
if(result)
putchar(i + 'a');
}
if(!flag)
puts("Harmonious");
return 0;
}
/*
4
abccddadca
*/
1313. 老曹骑士
题目
Description
我们的主角——老曹陨落于国际象棋棋盘,成为了一位老曹骑士,于是,他开始走“日”字型路线。
在一张N*N的棋盘上,有K只邪恶的河蟹,骑士曹现在要消灭这些河蟹。
要求曹从任意一只河蟹出发,通过他“日”字型的跳跃,到达这K个点至少一次,并最终回到起点。
现在已知棋盘的大小N,和这K只河蟹的位置(棋盘的左上角坐标记为(1,1),右下角坐标记为(N,N)。
询问:曹最少要跳多少步。Input
第一行:两个整数,N,K(4<=N<=20,1<=K<=10)
接下来K行:每行两个整数X,Y,表示河蟹所在位置。Output
一个整数,表示曹所需要条的最少步数。
Sample Input
8 3 2 3 4 5 6 7
Sample Output
12
思路
状压DP裸题,设\(f_{i,j}\)表示当前消灭的河蟹的集合为\(i\)(状压),最后消灭的一只河蟹编号为\(j\),外面在套一个循环枚举起点即可.
关于两点距离的预处理,跑一遍Floyd即可.
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int deltaxy[8][2] = {
1,2 , 1,-2 , 2,1 , 2,-1 , -1,2 , -1,-2 , -2,1 , -2,-1
};
const int N = 25 , M = 25;
int n , m;
int x[M] , y[M];
int id[N][N] , cnt;
int map[N * N][N * N];
#define dis(x0 , y0 , x1 , y1) map[id[x0][y0]][id[x1][y1]]
int f[1 << 12][12];
bool vis[1 << 12][12];
struct State {
int set , laspos ;
};
State make(int set , int laspos) {
State tmp;
tmp.set = set , tmp.laspos = laspos;
return tmp;
}
std::queue <State> q;
int min(int a, int b) {
return a < b ? a : b;
}
int main() {
n = read() , m = read();
for(int i = 0 ; i < n ; i++)
for(int j = 0 ; j < n ; j++)
id[i][j] = cnt++;
for(int i = 0 ; i < m ; i++)
x[i] = read() - 1 , y[i] = read() - 1;
memset(map , 0x3f , sizeof(map));
for(int i = 0 ; i < n ; i++)
for(int j = 0 ; j < n ; j++) {
for(int k = 0 ; k < 8 ; k++) {
int gx = i + deltaxy[k][0] , gy = j + deltaxy[k][1];
if(gx >= 0 && gy >= 0 && gx < n && gy < n)
dis(i , j , gx , gy) = 1;
}
}
for(int k = 0 ; k < cnt ; k++)//Floyd
for(int i = 0 ; i < cnt ; i++)
for(int j = 0 ; j < cnt ; j++)
if(map[i][j] > map[i][k] + map[k][j])
map[i][j] = map[i][k] + map[k][j];
if(m == 1) {
std::cout << 0;
return 0;
}
int ans = 0x3fffffff;
for(int begin = 0 ; begin < m ; begin++) {
memset(f, 0x3f , sizeof(f));
f[1 << begin][begin] = 0 , q.push(make(1 << begin , begin)) , vis[1 << begin][begin] = true;
while(!q.empty()) {
State k = q.front();
q.pop();
for(int i = 0 ; i < m ; i++)
if((k.set & (1 << i)) == 0) {
int newset = (k.set | (1 << i));
if(!vis[newset][i])
q.push(make(newset , i)) , vis[newset][i] = true;
f[newset][i] = min(
f[newset][i] ,
f[k.set][k.laspos] + dis(x[k.laspos] , y[k.laspos] , x[i] , y[i])
);
}
}
for(int i = 0 ; i < m ; i++)
ans = min(ans , f[(1 << m) - 1][i] + dis(x[begin] , y[begin] , x[i] , y[i]));
}
std::cout << ans;
return 0;
}
1314. 稳定的数字
题目
Description
定义一种操作,经过该操作,可以得到一个数中所有数字的乘积(原数至少要是个两位数)。比如679经过一次操作可以得到378。 你的任务读入一个数,计算经过一次操作得到它的最小的数是多少。
Input
仅一行,表示一个十进制数。数的长度可能到达1000位。
Output
经过一次操作可以得到这个数的最小的数。如果没有这样的数,输出“There is no such number.”(不包括引号)
Sample Input
输入1: 0 输入2: 18 输入3: 51
Sample Output
输出1: 10 输出2: 29 输出3: There is no such number.
思路
又是一道水题.
因为最后的数字最小,就要位数最小,把输入的数分解\(1\sim 9\)的因数即可(注意,像数字18,我们分解为\(9\times2\)而不是\(3\times3\times2\),以减少位数).
网上抠了个高精模板.
代码
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
//ABOUT "BigIntTiny":
// filename: bigint_tiny.h
// author: baobaobear
// create date: 2021-02-10
// This library is compatible with C++03
// https://github.com/Baobaobear/MiniBigInteger
struct BigIntTiny {
int sign;
std::vector<int> v;
BigIntTiny() : sign(1) {}
BigIntTiny(const std::string &s) {
*this = s;
}
BigIntTiny(int v) {
char buf[21];
sprintf(buf, "%d", v);
*this = buf;
}
void zip(int unzip) {
if (unzip == 0) {
for (int i = 0; i < (int)v.size(); i++)
v[i] = get_pos(i * 4) + get_pos(i * 4 + 1) * 10 + get_pos(i * 4 + 2) * 100 + get_pos(i * 4 + 3) * 1000;
} else
for (int i = (v.resize(v.size() * 4), (int)v.size() - 1), a; i >= 0; i--)
a = (i % 4 >= 2) ? v[i / 4] / 100 : v[i / 4] % 100, v[i] = (i & 1) ? a / 10 : a % 10;
setsign(1, 1);
}
int get_pos(unsigned pos) const {
return pos >= v.size() ? 0 : v[pos];
}
BigIntTiny &setsign(int newsign, int rev) {
for (int i = (int)v.size() - 1; i > 0 && v[i] == 0; i--)
v.erase(v.begin() + i);
sign = (v.size() == 0 || (v.size() == 1 && v[0] == 0)) ? 1 : (rev ? newsign * sign : newsign);
return *this;
}
std::string to_str() const {
BigIntTiny b = *this;
std::string s;
for (int i = (b.zip(1), 0); i < (int)b.v.size(); ++i)
s += char(*(b.v.rbegin() + i) + '0');
return (sign < 0 ? "-" : "") + (s.empty() ? std::string("0") : s);
}
bool absless(const BigIntTiny &b) const {
if (v.size() != b.v.size()) return v.size() < b.v.size();
for (int i = (int)v.size() - 1; i >= 0; i--)
if (v[i] != b.v[i]) return v[i] < b.v[i];
return false;
}
BigIntTiny operator-() const {
BigIntTiny c = *this;
c.sign = (v.size() > 1 || v[0]) ? -c.sign : 1;
return c;
}
BigIntTiny &operator=(const std::string &s) {
if (s[0] == '-')
*this = s.substr(1);
else {
for (int i = (v.clear(), 0); i < (int)s.size(); ++i)
v.push_back(*(s.rbegin() + i) - '0');
zip(0);
}
return setsign(s[0] == '-' ? -1 : 1, sign = 1);
}
bool operator<(const BigIntTiny &b) const {
return sign != b.sign ? sign < b.sign : (sign == 1 ? absless(b) : b.absless(*this));
}
bool operator==(const BigIntTiny &b) const {
return v == b.v && sign == b.sign;
}
BigIntTiny &operator+=(const BigIntTiny &b) {
if (sign != b.sign) return *this = (*this) - -b;
v.resize(std::max(v.size(), b.v.size()) + 1);
for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {
carry += v[i] + b.get_pos(i);
v[i] = carry % 10000, carry /= 10000;
}
return setsign(sign, 0);
}
BigIntTiny operator+(const BigIntTiny &b) const {
BigIntTiny c = *this;
return c += b;
}
void add_mul(const BigIntTiny &b, int mul) {
v.resize(std::max(v.size(), b.v.size()) + 2);
for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {
carry += v[i] + b.get_pos(i) * mul;
v[i] = carry % 10000, carry /= 10000;
}
}
BigIntTiny operator-(const BigIntTiny &b) const {
if (sign != b.sign) return (*this) + -b;
if (absless(b)) return -(b - *this);
BigIntTiny c;
for (int i = 0, borrow = 0; i < (int)v.size(); i++) {
borrow += v[i] - b.get_pos(i);
c.v.push_back(borrow);
c.v.back() -= 10000 * (borrow >>= 31);
}
return c.setsign(sign, 0);
}
BigIntTiny operator*(const BigIntTiny &b) const {
if (b < *this) return b **this;
BigIntTiny c, d = b;
for (int i = 0; i < (int)v.size(); i++, d.v.insert(d.v.begin(), 0))
c.add_mul(d, v[i]);
return c.setsign(sign * b.sign, 0);
}
BigIntTiny operator/(const BigIntTiny &b) const {
BigIntTiny c, d;
d.v.resize(v.size());
double db = 1.0 / (b.v.back() + (b.get_pos((unsigned)b.v.size() - 2) / 1e4) +
(b.get_pos((unsigned)b.v.size() - 3) + 1) / 1e8);
for (int i = (int)v.size() - 1; i >= 0; i--) {
c.v.insert(c.v.begin(), v[i]);
int m = (int)((c.get_pos((int)b.v.size()) * 10000 + c.get_pos((int)b.v.size() - 1)) * db);
c = c - b * m, d.v[i] += m;
while (!(c < b))
c = c - b, d.v[i] += 1;
}
return d.setsign(sign * b.sign, 0);
}
BigIntTiny operator%(const BigIntTiny &b) const {
return *this - *this / b * b;
}
bool operator>(const BigIntTiny &b) const {
return b < *this;
}
bool operator<=(const BigIntTiny &b) const {
return !(b < *this);
}
bool operator>=(const BigIntTiny &b) const {
return !(*this < b);
}
bool operator!=(const BigIntTiny &b) const {
return !(*this == b);
}
};
using namespace std;
BigIntTiny a;
char s[1010];
int c[1010];
int main() {
scanf("%s" , s);
if(strlen(s) == 1) {
putchar('1') , putchar(s[0]);
return 0;
}
a = s;
for(int i = 9 ; i >= 2 ; i--) {
while(a % i == 0)
a = a / i , ++c[i];
}
if(a > 1)
puts("There is no such number.");
else {
for(int i = 2 ; i <= 9 ; i++) {
for(int j = 1 ; j <= c[i] ; j++)
putchar(i + '0');
}
}
return 0;
}
封锁阳光大学
题目
题目描述
曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。
阳光大学的校园是一张由 \(n\) 个点构成的无向图,\(n\) 个点之间由 \(m\) 条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。
询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。
输入格式
第一行两个正整数,表示节点数和边数。
接下来 \(m\) 行,每行两个整数 \(u,v\),表示点 \(u\) 到点 \(v\) 之间有道路相连。输出格式
仅一行如果河蟹无法封锁所有道路,则输出
Impossible
,否则输出一个整数,表示最少需要多少只河蟹。输入输出样例
输入 #1
3 3 1 2 1 3 2 3
输出 #1
Impossible
输入 #2
3 2 1 2 2 3
输出 #2
1
说明/提示
【数据规模】
对于 \(100\%\) 的数据,\(1\le n \le 10^4\),\(1\le m \le 10^5\),保证没有重边。
思路
题意简述:\(n\)个点,01染色,同一条边相连的两个点不能染一种颜色,每个点都要染色,问最少要染多少个颜色1.
其实可以发现,只要一个点的颜色确定,整个连通块的颜色都确定了.
枚举即可.
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int N = 10010 , M = 100010;
struct EDGE {
int to , nxt;
}ed[M * 2];
int head[N];
void addedge(int u , int v) {
static int cnt;
++cnt;
ed[cnt].to = v , ed[cnt].nxt = head[u] , head[u] = cnt;
}
int n , m;
int color[N];
int dfs(int u) {
int sum = 0;
for(int i = head[u] ; i ; i = ed[i].nxt) {
int v = ed[i].to;
if(color[v] == -1) {
color[v] = color[u] ^ 1;//染相反颜色
int tmp = dfs(v);
if(tmp == -1)
return -1;
else
sum += tmp;
} else {
if(color[v] != color[u] ^ 1)
return -1;
}
}
return sum + color[u];
}
void clear(int u) {//清除染色
color[u] = -1;
for(int i = head[u] ; i ; i = ed[i].nxt) {
if(color[ed[i].to] != -1)
clear(ed[i].to);
}
}
int main() {
n = read() , m = read();
for(int i = 1 ; i <= m ; i++) {
int u = read() , v = read();
addedge(u , v);
addedge(v , u);
}
memset(color , -1 , sizeof(color));//-1表示未染色
int ans = 0;
for(int i = 1 ; i <= n ; i++)
if(color[i] == -1) {//这里写得比较丑,其实就是枚举第i个点的颜色,取更优的方案,其实也可以不用这么写,直接连通块点数一减好像就行
int tmp , tmp1;
color[i] = 0;
tmp = dfs(i);
tmp = tmp == -1 ? 0x3fffffff : tmp;
clear(i);
color[i] = 1;
tmp1 = dfs(i);
tmp1 = tmp1 == -1 ? 0x3fffffff : tmp1;
if(tmp == 0x3fffffff && tmp1 == 0x3fffffff) {
ans = -1;
break;
}
ans += min(tmp , tmp1);
}
if(ans == -1)
puts("Impossible");
else
cout << ans;
return 0;
}