• 【ybt金牌导航8-4-2】【luogu P2158】【POJ 3090】可见点数 / 仪仗队 / Visible Lattice Points


    可见点数 / 仪仗队 / Visible Lattice Points

    题目链接:ybt金牌导航8-4-2 / luogu P2158 / POJ 3090

    题目大意

    有一个网格,然后可以从 (0,0) 向其它整数点画一条线,但是中间不能经过其它整数点。

    问你可以画多少条线。

    思路

    在这里插入图片描述
    我们考虑根据这个图,看看能不能发现什么。

    然后你会发现它有对称性,就是左下-右上的线段是对称轴,它两边线段的分布情况和个数都对称。
    然后它这条线上也有一个符合的线。

    那你可以只求一种一边的个数,然后乘二加一,就是答案。

    然后你考虑一边的怎么弄。
    那你想到,如果不满足的线,它是怎么样的。
    可以想到,如果一个点 ((x,y)),有 (gcd(x,y) eq1),那必然至少会有点 ((x/gcd(x,y),y/gcd(x,y))) 在连向它的线段上,就不行了。

    那就变成了要 (gcd(x,y)=1)
    那你可以枚举 (x),然后就变成了求 (1sim x) 中与 (x) 互质的个数。
    那就是欧拉函数了,这题要算很多个,可以用线性筛来做。

    代码(ybt & luogu)

    Tips

    这里它网格的大小是 (n)((0,0)sim(n-1,n-1))
    一定要注意不是到 ((n,n)),它是小于而不是小于等于。

    #include<cstdio>
    #define ll long long
    
    using namespace std;
    
    int n, phi[40001], pr[40001];
    bool np[40001];
    ll ans;
    
    int main() {
    	scanf("%d", &n);
    	
    	phi[1] = 1;//求出 1~n 每个数的 phi 值
    	for (int i = 2; i < n; i++) {
    		if (!np[i]) {
    			phi[i] = i - 1;
    			pr[++pr[0]] = i;
    			
    		}
    		for (int j = 1; j <= pr[0] && pr[j] * i < n; j++) {
    			np[i * pr[j]] = 1;
    			if (i % pr[j] == 0) phi[i * pr[j]] = phi[i] * pr[j];//不止一个这样的因子,只有第一个要减一
    				else phi[i * pr[j]] = phi[i] * (pr[j] - 1);
    			if (i % pr[j] == 0) break;//第一次出现,要减一
    		}
    	}
    	
    	for (int i = 1; i < n; i++)//对称性,乘二
    		ans += 2ll * phi[i];
    	ans++;//中间的那一条
    	
    	printf("%lld", ans);
    	
    	return 0;
    }
    

    代码(POJ)

    Tips

    这道题有多组数据,而且是 ((0,0)sim(n,n))

    我们可以把 (varphi) 函数先预处理出来,然后再预处理乘二之后的前缀和,然后你就可以 (O(1)) 处理询问了。

    //由于思路相同,代码实现也差不多,就不再作注释了
    #include<cstdio>
    #define ll long long
    
    using namespace std;
    
    ll T, n, pr[40001];
    bool np[40001];
    ll phi[40001];
    
    int main() {
    	phi[1] = 1;
    	for (ll i = 2; i <= 40000; i++) {
    		if (!np[i]) {
    			phi[i] = i - 1;
    			pr[++pr[0]] = i;
    		}
    		for (ll j = 1; j <= pr[0] && pr[j] * i < 40000; j++) {
    			np[i * pr[j]] = 1;
    			if (i % pr[j] == 0) phi[i * pr[j]] = phi[i] * pr[j];
    				else phi[i * pr[j]] = phi[i] * (pr[j] - 1ll);
    			if (i % pr[j] == 0) break;
    		}
    	}
    	
    	for (ll i = 1; i <= 40000; i++)
    		phi[i] = phi[i - 1] + phi[i] * 2ll;
    	
    	
    	scanf("%lld", &T);
    	
    	for (ll i = 1; i <= T; i++) {
    		scanf("%lld", &n);
    		printf("%lld %lld %lld
    ", i, n, phi[n] + 1);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    【转载】Allegro Auto Rename器件反标注教程
    FPGA代码设计规范整理
    Cadence Allegro导网表的错误问题解决
    简单玩转Excel排序、筛选、分类汇总与数据透视表功能!
    zt一篇教会你写90%的shell脚本
    zt一篇教会你写90%的shell脚本
    TCP协议理解
    zt 改进TCP,阿里提出高速云网络拥塞控制协议HPCC
    zt TCP的困境与解决方案
    ztGoogle's BBR拥塞控制算法如何对抗丢包
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_8-4-2.html
Copyright © 2020-2023  润新知