杭电第八场 题解

比赛传送门
作者: fn


基本题

1006题 GCD Game / 最大公因数游戏

题目大意
爱丽丝和鲍勃在玩游戏。
给定 n 个数字。他们轮流操作,每次进行以下操作。

  1. 任意选择一个数字 a i a_i ai
  2. 任意选择另一个数 x x x ( 1 ≤ x < a i 1≤x<ai 1x<ai )。
  3. 将数字 a i a_i ai 替换为 g c d ( a i , x ) gcd(a_i,x) gcd(ai,x) 。 这里 g c d ( u , v ) gcd(u,v) gcd(u,v) 指的是 u u u v v v 的最大公约数。

当玩家一步都走不动时,他/她就输了这场比赛。 爱丽丝先走,她让你告诉她,如果双方都采取最优策略,谁会赢。

考察内容
NIM博弈,欧拉筛

分析
NIM博弈,把每个数看成一堆物品,物品个数就是每个数的质因子个数。用线性筛预处理 1e7 以内的质因子个数即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e7+10;

int n,m,a; //

int num[N];
int prm[N],cnt;
bool f[N];

void Euler_prime(int n) // 线性筛(欧拉筛) 
{
    for(int i=2;i<=n;i++)
    {
        if(!f[i])prm[++cnt]=i,num[i]=1;
        for(int j=1;j<=cnt && i*prm[j]<=n;j++)
        {
            f[i*prm[j]]=true;
            num[i*prm[j]]=num[i]+1; // 记录质因子个数 
            if(i%prm[j]==0)break;
        }
    }
}

int main(){ // 
	Euler_prime((int)1e7);
	
	int t; cin>>t;
	while(t--){
		scanf("%d",&n); // cin>>n;
		
		scanf("%d",&a); // cin>>a;
		int ans=num[a];
		for(int i=2;i<=n;i++){
			scanf("%d",&a); // cin>>a;
			ans^=num[a];
		}
		
		if(ans!=0) cout<<"Alice"<<endl; //  
		else cout<<"Bob"<<endl;
	}
}

1003题 Ink on paper / 纸上的墨水

题目大意
鲍勃不小心把几滴墨水洒在纸上了。 第 i i i 滴墨水的起始位置是 ( x i , y i ) (x_i,y_i) (xi,yi) ,以每秒0.5厘米的速度向外膨胀,呈现出一个圆形。
好奇的鲍勃想知道所有的墨水连接起来需要多长时间。 输出时间的平方。

考察内容
最小生成树,prim算法

分析
题面可以转化成在一个完全图上求最小生成树,直接使用Prim解决,复杂度 O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
using namespace std;

long long t,n,x[5005],y[5005],ans,dis[5005],vis[5005],mind;
long long fdis(int i,int j) {
    return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int main() {
    scanf("%lld",&t);
    while(t--) {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%lld %lld",&x[i],&y[i]);
        ans=0;
        memset(dis,0x6f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        int u;
        dis[1]=0;
        for(int i=1;i<=n;i++) {
            mind=9e18;
            for (int j = 1; j <= n; j++)
                if(!vis[j] && dis[j] < mind) mind=dis[j],u=j;
            vis[u]=1;
            ans=max(ans,dis[u]);
            for (int j = 1; j <= n; j++)
                if(!vis[j]) dis[j]=min(dis[j],fdis(u,j));
        }
        printf("%lld\n",ans);
    }
    return 0;
}

进阶题

1009题 Singing Superstar / 歌星

题目大意
给定一个母字符串和若干子字符串,求每个子字符串在母字符串中不相交的出现次数。

例:“abababa”中“aba”不相交的出现次数为2 。

考察内容
字符串哈希,Trie,AC自动机

分析
字符串题,对母串中所有长度小于等于30的串做字符串哈希,对相同哈希值的串暴力统计答
案,每个询问直接查询对应哈希值的答案。
也可以使用Trie/AC自动机来通过本题。

相似问题
ZOJ Searching the String

#include <bits/stdc++.h>
#define ll long long
using namespace std;

int read() { // 快读 
    int tot = 0, fh = 1;
    char c = getchar();
    while ((c < '0') || (c > '9')) {
        if (c == '-')fh = -1;
        c = getchar();
    }
    while ((c >= '0') && (c <= '9')) {
        tot = tot * 10 + c - '0';
        c = getchar();
    }
    return tot * fh;
}

const int maxn = 100010;
const int maxq = 30;
int T, opt, n, thi, len;
char s[maxn], a[maxn];
struct Trie {
    int son[maxn * 30][26], las[maxn * 30], fla[maxn * 30], r;
    void clear() {
        memset(son, 0, sizeof(son));
        memset(fla, 0, sizeof(fla));
        memset(las, 0, sizeof(las));
        r = 1;
    }
    int add(int thi, char x) {
        if (son[thi][x] == 0) {
            r++;
            son[thi][x] = r;
        }

        return son[thi][x];
    }
} tre;

int main() {
    T = read();
    while(T--){
        scanf("%s", s);
        len = strlen(s);
        tre.clear();

        for (int j = 0; j < len; j++) {
            thi = 1;

            for (int k = 0; k < min(len - j, maxq); k++) {
                thi = tre.add(thi, s[j + k] - 'a');

                if (tre.las[thi] <= j) {
                    tre.las[thi] = j + k + 1;
                    tre.fla[thi]++;
                }
            }
        }

        n = read();

        for (int i = 1; i <= n; i++) {
            scanf("%s", a);
            thi = 1;
            len = strlen(a);

            for (int j = 0; j < len; j++) {
                thi = tre.add(thi, a[j] - 'a');
            }

            printf("%d\n", tre.fla[thi]);
        }
    }
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值