2011-0058
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
简单来说,题目大意是,有一个等边三角形(边的内侧可以反射光线),顶点分别是A、B、C,一束光从A点沿某一固定角度射入,经过n次反射之后又从A点射出(注意光线可以从任一个顶点射出)。
问,给定n,有多少个射入角度(从A点射入)可以使光线恰好经过n次反射并最终从A点反射出。[[BR]]
解法:
把三角形扩展到整个平面,并建立一个斜角坐标系(AB为x轴正向,AC为y轴正向,夹角为60度),这样我们只需要在该坐标系的第一象限考虑问题。
首先所有点被分成三类,分别是A类,B类和C类(即分别对应不同顶点的反射像),不难发现(x,y)为A类点当且仅当((x%3==0&&(x+y)%3==0) 或者(x%3==1 && (x+y)%3==2)或者 (x%3==2 &&(x+y)%3==1)).[[BR]]
注意到若最终从(x,y)点射出,必然有gcd(x,y)=1,且反射次数为(x+y-1)+(x-1)+(y-1),三项分别是沿BC、AC和AB的反射次数[[BR]]
所以给定n,我们要求所有(x,y)点对的个数,满足[[BR]]
x>0,y>0,x+y=(n+3)/2,gcd(x,y)=1,(x,y)是A类点.[[BR]]
对(n+3)/2分解质因数,设为p1^s1^*p2^s2^*...*pt^st^.则求(n+3)/2以内满足条件的x(即所有不被p1,p2,...,pt整除且被3除余(3-((n+3)/2)%3))的个数即可,容斥原理。
程序:
{{{
#include<iostream>
#include<set>
#include<map>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<cstdio>
using namespace std;
const int PSIZE = 200050;
long long plist[PSIZE], pcount = 0;
bool prime(long long n) {
if ((n != 2 && !(n % 2)) || (n != 3 && !(n % 3)) || (n != 5 && !(n % 5)) || (n != 7 && !(n % 7))) {
return false;
}
for (long long i = 0; plist[i] * plist[i] <= n; ++i) {
if (n % plist[i] == 0) {
return false;
}
}
return n > 1;
}
void initPrime() {
plist[pcount++] = 2;
for (long long i = 3; i < PSIZE; i += 2) {
if (prime(i)) {
plist[pcount++] = i;
}
}
}
long long primeFactor(long long n, long long * f, long long * nf) {
long long cnt = 0;
long long n2 = (long long) sqrt((double) n);
for (long long i = 0; n > 1 && plist[i] <= n2; ++i) {
if (n % plist[i] == 0) {
for (nf[cnt] = 0; n % plist[i] == 0; n /= plist[i]) {
++nf[cnt];
}
f[cnt++] = plist[i];
}
}
if (n > 1) {
nf[cnt] = 1;
f[cnt++] = n;
}
return cnt;
}
int main()
{
long long n;
initPrime();
while(scanf("%lld",&n)!=EOF)
{
if(n%2==0)
cout<<0<<endl;
else
{
long long t=(n+3)/2;
if(t%3!=0)
{
// a=2 mod(3)
long long f[20],nf[20];
long long num=primeFactor(t,f,nf);
long long tem=1;
long long ans=0;
//cout<<t<<" "<<num<<endl;
for(long long i=0;i<(1<<num);i++)
{
tem=1;
long long symbol=1;
for(long long j=0;j<num;j++)
{
if(i&(1<<j))
{
tem*=f[j];
symbol*=-1;
}
}
long long s=0;
if((tem%3==2&&t%3==1)||(tem%3==1&&t%3==2))
{
s=2;
}
else
s=1;
//cout<<tem<<endl;
ans+=symbol*((((t-1)/tem)+s)/3);
//cout<<symbol*(((t/tem)+1)/3)<<endl;
//cout<<endl;
}
cout<<ans<<endl;
}
else if(t%3==0)
{
cout<<0<<endl;
}
}
}
return 0;
}
}}}
by quietsmile
简单来说,题目大意是,有一个等边三角形(边的内侧可以反射光线),顶点分别是A、B、C,一束光从A点沿某一固定角度射入,经过n次反射之后又从A点射出(注意光线可以从任一个顶点射出)。
问,给定n,有多少个射入角度(从A点射入)可以使光线恰好经过n次反射并最终从A点反射出。
解法:
把三角形扩展到整个平面,并建立一个斜角坐标系(AB为x轴正向,AC为y轴正向,夹角为60度),这样我们只需要在该坐标系的第一象限考虑问题。
首先所有点被分成三类,分别是A类,B类和C类(即分别对应不同顶点的反射像),不难发现(x,y)为A类点当且仅当((x%3==0&&(x+y)%3==0) 或者(x%3==1 && (x+y)%3==2)或者 (x%3==2 &&(x+y)%3==1)).
注意到若最终从(x,y)点射出,必然有gcd(x,y)=1,且反射次数为(x+y-1)+(x-1)+(y-1),三项分别是沿BC、AC和AB的反射次数
所以给定n,我们要求所有(x,y)点对的个数,满足
x>0,y>0,x+y=(n+3)/2,gcd(x,y)=1,(x,y)是A类点.
对(n+3)/2分解质因数,设为p1s1*p2s2*...*ptst.则求(n+3)/2以内满足条件的x(即所有不被p1,p2,...,pt整除且被3除余(3-((n+3)/2)%3))的个数即可,容斥原理。
程序:
#include<iostream>
#include<set>
#include<map>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<cstdio>
using namespace std;
const int PSIZE = 200050;
long long plist[PSIZE], pcount = 0;
bool prime(long long n) {
if ((n != 2 && !(n % 2)) || (n != 3 && !(n % 3)) || (n != 5 && !(n % 5)) || (n != 7 && !(n % 7))) {
return false;
}
for (long long i = 0; plist[i] * plist[i] <= n; ++i) {
if (n % plist[i] == 0) {
return false;
}
}
return n > 1;
}
void initPrime() {
plist[pcount++] = 2;
for (long long i = 3; i < PSIZE; i += 2) {
if (prime(i)) {
plist[pcount++] = i;
}
}
}
long long primeFactor(long long n, long long * f, long long * nf) {
long long cnt = 0;
long long n2 = (long long) sqrt((double) n);
for (long long i = 0; n > 1 && plist[i] <= n2; ++i) {
if (n % plist[i] == 0) {
for (nf[cnt] = 0; n % plist[i] == 0; n /= plist[i]) {
++nf[cnt];
}
f[cnt++] = plist[i];
}
}
if (n > 1) {
nf[cnt] = 1;
f[cnt++] = n;
}
return cnt;
}
int main()
{
long long n;
initPrime();
while(scanf("%lld",&n)!=EOF)
{
if(n%2==0)
cout<<0<<endl;
else
{
long long t=(n+3)/2;
if(t%3!=0)
{
// a=2 mod(3)
long long f[20],nf[20];
long long num=primeFactor(t,f,nf);
long long tem=1;
long long ans=0;
//cout<<t<<" "<<num<<endl;
for(long long i=0;i<(1<<num);i++)
{
tem=1;
long long symbol=1;
for(long long j=0;j<num;j++)
{
if(i&(1<<j))
{
tem*=f[j];
symbol*=-1;
}
}
long long s=0;
if((tem%3==2&&t%3==1)||(tem%3==1&&t%3==2))
{
s=2;
}
else
s=1;
//cout<<tem<<endl;
ans+=symbol*((((t-1)/tem)+s)/3);
//cout<<symbol*(((t/tem)+1)/3)<<endl;
//cout<<endl;
}
cout<<ans<<endl;
}
else if(t%3==0)
{
cout<<0<<endl;
}
}
}
return 0;
}
by quietsmile