2018-2019 ACM-ICPC, Asia Nanjing Regional Contest

2018-2019 ACM-ICPC, Asia Nanjing Regional Contest

题目 A B C D E F G H I J K L M
solved - - - - - -

✔:比赛时通过;:赛后通过;⚪:比赛时尝试了未通过;-:比赛时未尝试


A. Adrien and Austin

solved by Sstee1XD & Tryna. 0:36(+3)

题意 n n n个石子,每个石子从 1 1 1 n n n编号,每次可以取 1 1 1- k k k个连续的石子,问谁先取完。

题解:我们特判 n n n 0 0 0 k k k 1 1 1的情况,对于其余情况,先手都可以做出相应的选择达到必胜态。

#include
using namespace std;
int n, k;

int main() {
	scanf("%d %d", &n, &k);
	if(n == 0) puts("Austin");
	else if (k == 1 && n % 2 == 0) puts("Austin");
	else puts("Adrien");
    return 0;
}

D - Country Meow

solved by Tryna. (-)

题意: 求覆盖了所有点的最小球半径

题解: 裸题,跑一边模拟退火,代码中更新pos值的时候理解了很久。总的来说就是当前温度越高,向最远点移动的幅度越大,当前温度越低,向最远点移动的幅度越小

#include
using namespace std;    
#define inf 0x3f3f3f3f
#define eps 1e-8
const int maxn = 110;
const double startT = 100000;
struct Point{
	double x, y, z;
}P[maxn];
double Dist(Point a, Point b){
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));
}
int n;
double SA(){
	double delta = 0.98, T = startT, ans = inf;
	Point pos = {0, 0, 0};
	while(T > eps){
		Point maxP = P[1];
		for(int i = 2; i <= n; i++){
			if(Dist(pos, P[i]) > Dist(pos, maxP))
				maxP = P[i];
		}
		ans = min(ans, Dist(pos, maxP));
		pos.x += (maxP.x - pos.x) * T / startT;
		pos.y += (maxP.y - pos.y) * T / startT;
		pos.z += (maxP.z - pos.z) * T / startT;
		T *= delta;
	}
	return ans;
}
 
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%lf %lf %lf", &P[i].x, &P[i].y, &P[i].z);
	printf("%.15f\n", SA());
	return 0;
}

G - Pyramid

solved by Tryna. (-)

题意: 计算等边三角形的个数

题解: 打表找规律,打表的思路就是把三角形放入坐标系中,O(n^3)暴力枚举每个顶点,如果三条边长度相同就计数。
打完表发现前几个数是这样的: 0 1 5 15 35 70 126 210
下面的方法将会用到这里的知识:差分的应用
所以我们对上面那个数列不断差分。
0 1 5 15 35 70 126 210
 1 4 10 20 35 56 84
  3 6 10 15 21 28
   3  4  5  6  7
     1  1  1  1
      0  0  0
所以前n项和 S ( n ) = 1 C n + 1 2 + 3 C n + 1 3 + 3 C n + 1 4 + 1 C n + 1 5 S(n) = 1C_{n+1} ^ 2 + 3C_{n+1} ^ 3 + 3C_{n+1} ^ 4 + 1C_{n+1} ^ 5 S(n)=1Cn+12+3Cn+13+3Cn+14+1Cn+15
化简得: S ( n ) = n ∗ ( n + 1 ) ∗ ( n + 2 ) ∗ ( n + 3 ) ∗ ( n + 4 ) 120 S(n) = \frac{n*(n+1)*(n+2)*(n+3)*(n+4)}{120} S(n)=120n(n+1)(n+2)(n+3)(n+4)
所以第n项 f ( n ) = S ( n ) − S ( n − 1 ) f(n) = S(n) - S(n-1) f(n)=S(n)S(n1)
化简得: f ( n ) = n ∗ ( n + 1 ) ∗ ( n + 2 ) ∗ ( n + 3 ) 24 f(n) = \frac{n*(n+1)*(n+2)*(n+3)}{24} f(n)=24n(n+1)(n+2)(n+3)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;    
#define inf 0x3f3f3f3f
#define ll long long
#define eps 1e-10
#define endl "\n"
const int maxn = 1000010;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
int moven2[10][5] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}}; 
int moven1[10][5] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int quick_power(ll n, ll k){
	int ans=1;
	while(k){
		if(k&1){
			ans=(ans*n)%mod;
		}
		n=n*n%mod;
		k>>=1;
	}
	return ans%mod;
}
int t;
ll n;
int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(0);cout.tie(0);
	scanf("%d", &t);
	while(t--){
		scanf("%lld", &n);
		ll ans = quick_power((ll)24, mod - 2);
		ans = ans * n % mod * (n + 1) % mod * (n + 2) % mod * (n + 3) % mod;
		printf("%lld\n", ans);
	}
	return 0;
}

I. Magic Potion【二分图匹配】

solved by lllllan. 01:26(+)

题意: n个英雄和m个怪兽,每个英雄都有对应的可以击败的怪兽并且只能选择其中一只。现有k瓶药水,每个英雄最多喝一瓶然后可以多击败一只怪兽。问最多可以击败多少只怪兽。

题解: 相比最经典的匹配问题,一个男朋友对应一个女朋友,现在可以一夫多妻了【不是】。又不像多重匹配那么麻烦,每个英雄最多也只能击败两只怪兽而已。所以就是简单跑两次匹配就可以了。 然后注意有K瓶药水的限制。

#include
using namespace std;
#define _for(i, a, b) for(int i = (a); i <= (b); ++i)

const int N = 510;
int sum;
int n, m, k, t, x;
int vis[N], match[N];
vector<int> G[N];

bool dfs (int u) {
	for(auto v : G[u]) {
		if(match[v] == u) continue;
		if(!vis[v]) {
			vis[v] = 1;
			if(!match[v] || dfs(match[v])) {
				match[v] = u;
				return true;
			}
		}
	}
	return false;
}

void solve () {
	memset(match, 0, sizeof match);
	_for(i, 1, n) {
		memset(vis, 0, sizeof vis);
		if(dfs(i)) sum++;
	}
	for(int i = 1; i <= n && k; i++) {
		memset(vis, 0, sizeof vis);
		if(dfs(i)) sum++, k--;
	}
}

int main() {
	scanf("%d%d%d", &n, &m, &k);
	_for(i, 1, n) {
		scanf("%d", &t);
		while(t--) {
			scanf("%d", &x);
			G[i].push_back(x);
		}
	}
	solve();
	printf("%d\n", sum);
    return 0;
}

J. Prime Game

solved by Sstee1XD. 1:11(+)

题意:给定序列a,定义

  • m u l ( l , r ) = ∏ i = l r a i mul(l, r) = \prod_{i = l}^ra_i mul(l,r)=i=lrai
  • f a c ( l , r ) fac(l, r) fac(l,r) m u l ( l , r ) mul(l, r) mul(l,r)的不同质因子的数量

让你计算 ∑ i = 1 n ∑ j = i n f a c ( i , j ) \sum_{i = 1}^n\sum_{j = i}^nfac(i, j) i=1nj=infac(i,j)

题解:我们设 p r e v pre_v prev v v v最后出现的位置。对于 a i a_i ai的质因子 v v v,它对于答案的贡献为对前面的贡献加上对后面的贡献,即 ( n − i + 1 ) ∗ ( i − p r e v − 1 ) + n − i + 1 (n - i +1)*(i-pre_v-1)+n-i+1 (ni+1)(iprev1)+ni+1

#include
using namespace std;

#define endl "\n"
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err() {	cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
    cout << arg << ' '; err(args...);}

inline int rd() {
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e6 + 7, N = 1e3;
int prime[N + 10], cnt, flag[N + 10];
void isprime() {
	for (int i = 2; i <= N; ++i) {
		if (!flag[i]) {
			prime[++cnt] = i;
		}
		for (int j = 1; j <= cnt; ++j) {
			if (1ll * prime[j] * i > N) break;
			flag[prime[j] * i] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}
int n, pre[maxn], a[maxn];
vector<int>G[maxn];
void run() {
	for (int i = 1; i <= n; ++i) {
		a[i] = rd();
		if (G[a[i]].size() == 0 && a[i] != 1) {
			int tmp = a[i];
			for (int j = 1; j <= cnt; ++j) {
				if (tmp % prime[j] == 0) {
					G[a[i]].push_back(prime[j]);
					while (tmp % prime[j] == 0) {
						tmp /= prime[j];
					}
					if (tmp == 1) break;
				}
			}
			if (tmp > 1) G[a[i]].push_back(tmp);
		}
	}
	ll ans = 0;
	for (int i = 1; i <= n; ++i) {
		for (auto v : G[a[i]]) {
			ans += (ll)(n - i + 1) * (ll)(i - pre[v] - 1) + (ll)(n - i + 1);
			pre[v] = i;
		}
	}
	printf("%lld\n", ans);
}

int main() {
	isprime();
	n = rd();
	run();
    return 0;
}

K. Kangaroo Puzzle

solved by Sstee1XD. (-)

题意:每次可以选择上下左右四个方向,一起移动所有的 1 1 1,如果碰到 0 0 0或者墙就不动,最后要在 50000 50000 50000步内使所有 1 1 1聚在一起。题目保证1不会转圈圈。

题解:因为保证的存在,所以就直接随机输出 50000 50000 50000个方向就好了(为什么会这么玄学?)。

#include 
using namespace std;

int main() {
	string s;
	int n, m;
	cin >> n >> m;
	while (n--) cin >> s;
	for (int i = 1; i <= 50000; ++i) {
		int x = rand()%4;
		if (x == 0) putchar('U');
		if (x == 1) putchar('D');
		if (x == 2) putchar('R');
		if (x == 3) putchar('L');
	}
	return 0;
} 

M. Mediocre String Problem

solved by Sstee1XD. (-)

题意: 给你两个字符串 s s s t t t,对于 t t t中所有的前缀串 k k k,你要在 s s s中找到所有的子串 s i j s_{ij} sij,使得 s i j s_{ij} sij长度大于 k k k,并且将 s i j s_{ij} sij k k k拼接后得到的字符串为回文串。问你最终的数量和。

题解:为了让最后的字符串是回文串,对于前缀串 k k k,我们要先在 s s s中找到与前缀串 k k k一样的子串,再向后扩展,使得扩展的那部分字符串为回文串,即可实现。
我们先将 t t t进行反转,然后去遍历 s s s,那么现在的问题就转换成了找两个字符串的最长后缀(因为对于这部分所有的子串,他们的答案是一样的,所以最后求出来乘以最长后缀的长度就行了)。这里我们用哈希+二分来实现。
找出最长后缀后,我们假设当前遍历到 s i s_i si,那么接下来我们要求以 s i + 1 s_{i+1} si+1为起点的回文串数量。想到回文串就想到了马拉车。马拉车可以求以每个点为中心的最长回文半径,那么对于以这个点为终点,以 i i i - 最长回文半径的位置为起点,终点对于这段区间的贡献值为1。所以就转化成了区间修改,最终求每个点的权值的差分数组问题。稍微推一下就能从马拉车字符串推回到原字符串数组。

#include 
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

const int maxn=1e6 + 7;
const ull base = 131;
char s[maxn], t[maxn];
char ma[maxn<<1];
ll mp[maxn<<1], paw[maxn];
ull gap[maxn], hashS[maxn], hashT[maxn];

void init() {
	gap[0] = 1;
	for (int i = 1; i <= 1000000; ++i) {
		gap[i] = gap[i - 1] * base;
	}
}

void makeHash(char *s, ull *hash) {
	int len = strlen(s);
	for (int i = 0; i < len; ++i) {
		hash[i + 1] = hash[i] * base + (ull)s[i];
	}
}

ull getHash(int l, int r, ull *hash) {
	return hash[r + 1] - hash[l] * gap[r - l + 1];
}

void manacher(char *s){
	int len = strlen(s);
	int l=0;
	ma[l++]='$';
	ma[l++]='#';
	for(int i = 0;i < len; ++i){
		ma[l++] = s[i];
		ma[l++] = '#';
	}
	ma[l] = 0;
	ll mx = 0, id = 0;
	for (int i = 0; i < l; ++i){
		mp[i] = mx > i? min(mp[2 * id - i], mx - i) : 1;
		while (ma[i + mp[i]] == ma[i - mp[i]])	mp[i]++;
		if(i + mp[i] > mx){
			mx = i + mp[i];
			id = i;
		}
	}
	for (int i = 1; i < l; i++) {
		paw[(i >> 1) - (mp[i] >> 1)]++;
		paw[(i >> 1)]--;
	}
	for (int i = 1; i < len; ++i) {
		paw[i] += paw[i - 1];
	}
}

void solve() {
	int len = strlen(s);
	int lent = strlen(t);
	int l, r, mid;
	ll ans = 0;
	ll maxx;
	for (int i = 0; i < len - 1; ++i) {
		l = 1, r = i + 1;
		maxx = 0;
		while (l <= r) {
			mid = l + r >> 1;
			if (getHash(i - mid + 1, i, hashS) == getHash(lent - mid, lent - 1, hashT)) {
				maxx = mid;
				l = mid + 1;
			}
			else r = mid - 1;
		}
		ans += maxx * paw[i + 1];
	}
	printf("%lld\n", ans);
}

int main(){
	init();
	scanf("%s %s", s, t);
	reverse(t, t + strlen(t));
	manacher(s);
	makeHash(s, hashS);
	makeHash(t, hashT);
	solve();
	return 0;
}

你可能感兴趣的:(My,team)