【SPOJ 7001】VLATTICE

题目传送门:http://www.spoj.com/problems/VLATTICE/
离线版题目:http://paste.ubuntu.com/20300942/

仪仗队升级版!
然而还是不会QAQ 还是可耻地看了题解QAQ

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 1000000+9;
const int MX = 1000000;

int pri[MAXN],tot,mu[MAXN];
bitset<MAXN> tag;

inline void Get_mu(){
	mu[1] = 1;
	for (int i=2;i<=MX;i++) {
		if (!tag[i]) pri[++tot] = i, mu[i] = -1;
		for (int j=1;j<=tot && i*pri[j]<=MX;j++){
			tag[i*pri[j]] = 1;
			if (i % pri[j]) mu[i*pri[j]] = -mu[i];
			else {mu[i*pri[j]]=0; break;}
		}
	}
	for (int i=1;i<=MX;i++) mu[i] += mu[i-1];
}

int main(){
	Get_mu(); int n,T,tmp; cin>>T;
	while (T--) {
		scanf("%d",&n); LL vout = 0;
		for (int i=1;i<=n;i=tmp+1){
			tmp = n/(n/i);
			vout += (LL)(mu[tmp]-mu[i-1])*(n/i)*(n/i);
		} vout = vout*3+3;
		for (int i=1;i<=n;i=tmp+1){
			tmp = n/(n/i);
			vout += (LL)(mu[tmp]-mu[i-1])*(n/i)*(n/i)*(n/i);
		}
		printf("%lld\n",vout); 
	}
	return 0;
} 

—– UPD 2016.9.21 —–
今天和同学们讨论这个题目的时候
发现,这题根本不需要莫比乌斯反演嘛 (╯‵□′)╯︵┻━┻
\(\sum\limits_{i = 1}^n {\sum\limits_{j = 1}^n {\sum\limits_{k = 1}^n {[\gcd (i,j,k) = 1] = } } } \sum\limits_{i = 1}^n {\sum\limits_{j = 1}^n {\sum\limits_{k = 1}^n {\sum\limits_{d|\gcd (i,j,k)} {\mu (d)} = \sum\limits_{d = 1}^n {\mu (d) \cdot {{\left\lfloor {\frac{n}{d}} \right\rfloor }^3}} } } } \)

【BZOJ 2820】YY的GCD

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2820
离线版题目:http://paste.ubuntu.com/20287584/

这个东西嘛,如果没有多组询问的话,枚举质数就可以水过去
但有多组询问QAQ
前排膜拜hzwer:http://hzwer.com/6142.html
现在身心俱疲,什么都不想说了 累…..

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 10000000+9;
const int MX = 10000000;

int pri[MAXN],tot,fuc[MAXN],mu[MAXN];
bitset<MAXN> tag;

inline void Get_Fuc(){
	fuc[1] = 0;
	for (int i=2;i<=MX;i++){
		if (!tag[i]) pri[++tot] = i, fuc[i] = 1, mu[i] = -1;
		for (int j=1;j<=tot && i*pri[j]<=MX;j++){
			tag[i*pri[j]] = 1;
			if (i % pri[j]) fuc[i*pri[j]] = -fuc[i] + mu[i], mu[i*pri[j]] = -mu[i];
			else {fuc[i*pri[j]] = mu[i]; mu[i] = 0; break;}
		}
	}
	for (int i=2;i<=MX;i++) fuc[i] += fuc[i-1];
}

int main(){
	Get_Fuc(); int T; cin>>T;
	while (T--) { LL vout = 0;
		int n,m,tmp; scanf("%d%d",&n,&m);
		if (n > m) swap(n,m);
		for (int i=1;i<=n;i=tmp+1){
			tmp = min(n/(n/i),m/(m/i));
			vout += (LL)(fuc[tmp]-fuc[i-1])*(n/i)*(m/i);
		}
		printf("%lld\n",vout);
	}
	return 0;
} 

【BZOJ 2705】[SDOI2012] Longge的问题

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2705
离线版题目:http://paste.ubuntu.com/20283268/

这题真的是蜜汁复杂度。
自己想的时候想到了,然而没敢写QAQ

首先是枚举gcd,然后搞phi或者mu
这个很显然,但只能过60%的数据

因为这个题是求gcd(i,n),所以gcd的取值只会有σ(n)个(有一个是定值),也就是n的因数的个数
然后用sqrt(n)来求每个因数的phi()

这样的话,时间复杂度上界是(sqrt(n))^2=O(n)的
这题n=10^9那不T到死QAQ
然而万能的vfk告诉我们,10^9内σ(n)最大为1000的样子,这样就不会T了QAQ
前排膜拜vfk:http://vfleaking.blog.163.com/blog/static/174807634201341913040467/
前排膜拜hht:http://techotaku.lofter.com/post/4856f0_634219b
ps:hht告诉我们,除数函数可以线筛,然而一脸懵逼QAQ

然而这题的还有一个trick,求phi()那里,还可以dfs算QAQ
复杂度更优!快4倍的样子
DFS-version:https://oi.abcdabcd987.com/eight-gcd-problems/(我就偷懒不写啦!)
original-version:

#include<iostream>
#include<cstdio>
#include<cmath>
#define LL long long
using namespace std;

inline LL Get_Phi(LL n){
	LL ret = n;
	for (int i=2,lim=ceil(sqrt(n));i<=lim;i++) if (n % i == 0) {
		ret = ret*(i-1)/i;
		while (n % i == 0) n /= i;
	}
	if (n > 1) ret = ret*(n-1)/n;
	return ret;
}

int main(){
	LL n,vout=0; scanf("%lld",&n);
	for (int i=1,lim=ceil(sqrt(n));i<=lim;i++) if (n%i == 0) {
		vout += (LL)i*Get_Phi(n/i);
		if (i*i < n) vout += (LL)(n/i)*Get_Phi(i);
	}
	printf("%lld\n",vout);
	return 0;
} 

—————————— UPD 2017.4.8 ——————————
找到这题的爸爸了:BZOJ 4802
也是求$\phi (n)$但$n \le 10^{18}$

【BZOJ 2190】[SDOI2008] 仪仗队

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2190
离线版题目:http://paste.ubuntu.com/20271981/

这个是“能量采集”的弱化版,所以mu和phi都可以很简单做
线筛写错了,wa了好久QAQ

#include<iostream>
#include<cstdio>
#include<bitset>
using namespace std;
 
const int MAXN = 40000+9;
 
int n,pri[MAXN],tot,phi[MAXN];
bitset<MAXN> tag;
 
inline void Get_Phi(){
    phi[1] = 1;
    for (int i=2;i<=n;i++){
        if (!tag[i]) pri[++tot] = i, phi[i] = i-1;
        for (int j=1;j<=tot && pri[j]*i<=n;j++) {
            tag[i*pri[j]] = 1;
            if (i % pri[j]) phi[i*pri[j]] = phi[i]*(pri[j]-1);
            else {phi[i*pri[j]] = phi[i]*pri[j]; break;} 
        }
    }
    for (int i=2;i<=n;i++) phi[i] += phi[i-1];
}
 
int main(){
    scanf("%d",&n); n-=1;
    Get_Phi();
    printf("%d\n",phi[n]*2-1+2);
    return 0;
} 

【BZOJ 2005】[Noi2010] 能量采集 Advance

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2005
前传传送门:http://oi.cyo.ng/?p=477

之前%JCVB的没有成功。
今天又来%,基本上有思路了,真的是_(:з」∠)_

引理:\(\varphi (n) = \sum\limits_{d|n} {\frac{n}{d} \cdot \mu (d)}\)
证明:\(\varphi (n) = \sum\limits_{i = 1}^n {[\gcd (i,n) = = 1] = \sum\limits_{i = 1}^n {\sum\limits_{d|i} {\sum\limits_{d|n} {\mu (d) = \sum\limits_{d|n} {\mu (d) \cdot \sum\limits_{i = 1}^n {[\gcd (i,n) = = i] = } } } } } } \sum\limits_{d|n} {\mu (d) \cdot \frac{n}{d}}\)

我们之前的式子是:\(\sum\limits_{d = 1}^n {\sum\limits_{k = 1}^{\left\lfloor {\frac{n}{d}} \right\rfloor } {d \cdot \mu (k) \cdot \left\lfloor {\frac{n}{{d \cdot k}}} \right\rfloor } } \cdot \left\lfloor {\frac{m}{{d \cdot k}}} \right\rfloor\)
注意看好,我要变形啦!( •̀ ω •́ )y
不妨设:D=k*d,则有原式=\(\sum\limits_{D = 1}^n {\sum\limits_{d|D} {d \cdot \mu (\frac{D}{d}) \cdot } } \left\lfloor {\frac{n}{D}} \right\rfloor \cdot \left\lfloor {\frac{m}{D}} \right\rfloor \)
根据引理可以进一步简化为:\(\sum\limits_{D = 1}^n {\varphi ({\rm{D}})} \cdot \left\lfloor {\frac{n}{D}} \right\rfloor \cdot \left\lfloor {\frac{m}{D}} \right\rfloor \)
然后就可以线筛出欧拉函数后,\(\sqrt n \)分块就好,总时间复杂度:O(n)

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 100000+9;

int n,m,pri[MAXN],tot,tmp;
LL phi[MAXN],vout;
bitset<MAXN> tag;

inline void Get_Phi(){
	phi[1] = 1;
	for (int i=2;i<=m;i++){
		if (!tag[i]) pri[++tot] = i, phi[i] = i-1;
		for (int j=1;j<=tot && pri[j]*i<=m;j++){
			tag[i*pri[j]] = 1;
			if (i % pri[j]) phi[i*pri[j]] = phi[i]*(pri[j]-1);
			else {phi[i*pri[j]] = phi[i]*pri[j]; break;}
		}
	}
	for (int i=1;i<=m;i++) phi[i] += phi[i-1];
}

int main(){
	scanf("%d%d",&n,&m);
	if (n > m) swap(n, m); Get_Phi();
	for (int i=1;i<=n;i=tmp+1){
		tmp = min(n/(n/i),m/(m/i));
		vout += (LL)(phi[tmp]-phi[i-1])*(n/i)*(m/i);
	}
	printf("%lld\n",vout*2-(LL)n*m);
	return 0;
} 

哦,对了,这类直线上点的个数的问题,有一个神奇的结论:
(x,y)这个点到原点的连线上整点的个数为gcd(x,y)
为什么会有这个结论呢?
因为这个相当于把线段分成了完全相同的gcd(x,y)段
或者可以这样理解:
离原点最近的一个肯定是(x/gcd(x,y),y/gcd(x,y))
之后的每一整点都是他的整数倍,所以总共有gcd(x,y)个

—————————— UPD 2017.2.1 ——————————
今天又看了看这个玩意儿,为什么要$\sqrt n$分块QwQ
暴力不也这复杂度吗……

而且完全不知道$\sum\limits_{d|n} {\frac{n}{d} \cdot \mu (d)}$是$\varphi (n)$也完全可以做啊!
根据狄利克雷卷积,可以知道这货是积性函数,然后再推一推发现可以线筛,然后直接做就好啊!

—————————— UPD 2017.7.3 ——————————
我发现自己之前数学好强
现在看这个题已经不会了qwq

【BZOJ 2818】Gcd

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2818
离线版题目:http://paste.ubuntu.com/20184097/

这个题目,很容易想到枚举素数,然后求gcd=1的对数
用莫比乌斯函数+分段来搞的话,好像很常规,时间复杂度O(ln(n)*sqrt(n)+n)
然后就是看到这篇博客,发现可以直接用phi函数搞QAQ,时间复杂度O(ln(n)+n)
因为phi前缀和那里必须用long long搞得实际运行时间比莫比乌斯函数还要慢QAQ
莫比乌斯函数版本:

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 10000000+9;

int pri[MAXN],tot,mu[MAXN],n;
bitset<MAXN> tag;

inline void Get_mu(){
	mu[1] = 1;
	for (int i=2;i<=n;i++){
		if (!tag[i]) pri[++tot] = i, mu[i] = -1;
		for (int j=1;j<=tot && pri[j]*i <= n;j++){
			tag[i*pri[j]] = 1;
			if (i % pri[j]) mu[i*pri[j]] = -mu[i];
			else {mu[i*pri[j]] = 0; break;}
		}
	}
	for (int i=1;i<=n;i++) mu[i] += mu[i-1];
}

int main(){ 
	scanf("%d",&n); Get_mu(); LL vout = 0;
	for (int j=1;j<=tot && pri[j] <= n;j++) {
		int k = pri[j], a=n/k;
		for (int i=1,tmp;i<=a;i=tmp+1)
			tmp = a/(a/i),
			vout += (LL)(mu[tmp]-mu[i-1])*(a/i)*(a/i);
	}
	printf("%lld\n",vout);
	return 0;
} 

欧拉函数版本:

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 10000000+9;

int pri[MAXN],tot,n;
LL phi[MAXN],vout=0;
bitset<MAXN> tag; 

inline void Get_Phi(){
	phi[1] = 1;
	for (int i=2;i<=n;i++){
		if (!tag[i]) pri[++tot] = i, phi[i] = i-1;
		for (int j=1;j<=tot && pri[j]*i<=n;j++){
			tag[pri[j]*i] = 1;
			if (i % pri[j]) phi[i*pri[j]] = phi[i]*(pri[j]-1);
			else {phi[i*pri[j]] = phi[i]*pri[j]; break;}
		}
	}
	for (int i=1;i<=n;i++) phi[i] += phi[i-1];
}

int main(){
	scanf("%d",&n); Get_Phi(); 
	for (int i=1;i<=tot && pri[i] <= n;i++) 
		vout += phi[n/pri[i]]*2 - 1;
	printf("%lld\n",vout);
	return 0;
} 

【BZOJ 2005】[Noi2010] 能量采集

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2005
离线版题目:http://paste.ubuntu.com/20171175/

这题,O(n^0.5+n)的做法我不会QAQ
%JCVB,看了半小时,还是不懂QAQ
只会O(n^1.5+n)的做法:
枚举gcd(i,j)的值,然后就和“Zap”一样了
居然没有T,好感动இ௰இ

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 100000+9;
const int MX = 100000;

int pri[MAXN],tot,mu[MAXN],sum[MAXN];
bitset<MAXN> tag;

inline void Get_mu(){
	mu[1] = 1;
	for (int i=2;i<=MX;i++){
		if (!tag[i]) pri[++tot] = i, mu[i] = -1;
		for (int j=1;j<=tot && pri[j]*i<=MX;j++) {
			tag[pri[j]*i] = 1;
			if (i % pri[j]) mu[i*pri[j]] = -mu[i];
			else {mu[i*pri[j]] = 0; break;}
		}
	}
	for (int i=1;i<=MX;i++) sum[i] = sum[i-1] + mu[i];
}

int main(){
	Get_mu(); int n,m,a,b,tmp;LL vout=0;
	scanf("%d%d",&n,&m); if (n > m) swap(n,m);
	for (int k=1;k<=n;k++) {
		a = n/k; b = m/k;
		for (int i=1;i<=a;i=tmp+1){
			tmp = min(a/(a/i),b/(b/i));
			vout += (LL)k*(sum[tmp]-sum[i-1])*(a/i)*(b/i);
		}
	}
	printf("%lld\n",vout*2-(LL)n*m);
	return 0;
} 

【BZOJ 2301】[HAOI2011] Problem b

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2301
离线版题目:http://paste.ubuntu.com/20155288/

这个题目和1101简直一毛一样QAQ
就是要减一减。貌似黄学长的做法是容斥一小下。
然而我比较笨,不会容斥。于是就只能暴力搞一搞。
结果一言不合就 Rank 3 QAQ
mobimus_problem_b

#include<iostream>
#include<cstdio>
#include<bitset>
using namespace std;

const int MAXN = 50000+9;
const int MX = 50000;

bitset<MAXN> tag;
int pri[MAXN],tot,mu[MAXN],sum[MAXN];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline void Get_mu(){
	mu[1] = 1;
	for (int i=2;i<=MX;i++) {
		if (!tag[i]) pri[++tot] = i, mu[i] = -1;
		for (int j=1;j<=tot && pri[j]*i<=MX;j++) {
			tag[pri[j]*i] = 1;
			if (i % pri[j]) mu[i*pri[j]] = -mu[i];
			else {mu[i*pri[j]] = 0; break;}	
		}
	}
	for (int i=1;i<=MX;i++) sum[i] = sum[i-1] + mu[i];
}

int main(){
	Get_mu(); int T = read();
	while (T--) {
		int a=read()-1,b=read(),c=read()-1,d=read(),k=read(),vout;
		a /= k; b /= k; c /= k; d /= k; vout = 0;
		for (int i=1,lim=min(b,d),tmp;i<=lim;i=tmp+1){
			tmp = min(b/(b/i),d/(d/i));
			if (a/i) tmp = min(tmp, a/(a/i));
			if (c/i) tmp = min(tmp, c/(c/i));
			vout += (sum[tmp]-sum[i-1])*(b/i-a/i)*(d/i-c/i);
		}
		printf("%d\n",vout);
	}
	return 0;
} 

【BZOJ 1101】[POI2007] Zap

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1101
离线版题目:http://paste.ubuntu.com/20151271/

莫比乌斯反演第一题! 1A! *★,°*:.☆\( ̄▽ ̄)/$:*.°★*
然而,之前翻来覆去看题解不下5遍QAQ

一开始想拿phi函数来搞,结果发现搞不了。
于是还是老老实实搞莫比乌斯:
不难发现,我们实际是要求这个东西:\(\sum\limits_{i = 1}^{\frac{a}{d}} {\sum\limits_{j = 1}^{\frac{b}{d}} {[gcd(i,j) = 1]} } \)
然后利用这个式子:\(\sum\limits_{d|n} {\mu (d) = \left\{ \begin{array}{l}1\left( {n = 1} \right)\\0(n > 1)\end{array} \right.} \)
就可以化成下面这个式子:\(\sum\limits_{i = 1}^{\frac{a}{d}} {\sum\limits_{j = 1}^{\frac{b}{d}} {\sum\limits_{k|\gcd (i,j)} {\mu (k)} } } \)
更进一步,因为 k|gcd(i,j)\( \Leftrightarrow \)k|i && k|j ,所以就可以化成这个式子:\(\sum\limits_{i = 1}^{\frac{a}{d}} {\sum\limits_{j = 1}^{\frac{b}{d}} {\sum\limits_{k|i} {\sum\limits_{k|j} {\mu (k)} } } } \)
然后把莫比乌斯函数前提,得到:\(\sum\limits_{k = 1}^{\min (\frac{a}{d},\frac{b}{d})} {\mu (k)\sum\limits_{i = 1}^{\frac{a}{d}} {\sum\limits_{j = 1}^{\frac{b}{d}} {\sum\limits_{k|i} {\sum\limits_{k|j} 1 } } } } \)
最后化简得到:\(\sum\limits_{k = 1}^{\min (\frac{a}{d},\frac{b}{d})} {\mu (k)\left\lfloor {\frac{a}{{d \cdot k}}} \right\rfloor \left\lfloor {\frac{b}{{d \cdot k}}} \right\rfloor } \)
然后就是分块了,因为\({\left\lfloor {\frac{a}{{d \cdot k}}} \right\rfloor }\)只会有\(\sqrt a \)种取值

#include<iostream>
#include<cstdio>
#include<bitset>
using namespace std;

const int MAXN = 50000+9;
const int MX = 50000;

int mu[MAXN],pri[MAXN],tot,sum[MAXN];
bitset<MAXN> tag;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline void Get_mu(){
	mu[1] = 1;
	for (int i=2;i<=MX;i++){
		if (!tag[i]) pri[++tot] = i, mu[i] = -1;
		for (int j=1;j<=tot && pri[j]*i <= MX;j++) {
			tag[pri[j]*i] = 1;
			if (i % pri[j]) mu[pri[j]*i] = -mu[i];
			else {mu[pri[j]*i] = 0; break;}
		}
	}
	for (int i=1;i<=MX;i++) sum[i] = sum[i-1] + mu[i];
}

int main(){
	Get_mu();
	int T = read(),a,b,k,ret; 
	while (T--) {
		a = read(); b = read(); k = read();
		a = a/k; b = b/k; ret = 0;
		for (int i=1,lim=min(a,b),tmp;i<=lim;i=tmp+1){
			tmp = min(a/(a/i),b/(b/i));
			ret += (sum[tmp]-sum[i-1])*(a/i)*(b/i);
		}
		printf("%d\n",ret);
	}
	return 0;
} 

【POJ 1284】Primitive Roots

题目传送门:http://poj.org/problem?id=1284

题目大意:给一个素数(>3),求原根的个数。
相关定理:如果一个数a有原根,则个数为\(\varphi (\varphi (a))\\)
定理证明:我不会QAQ,可以参考:wiki中的性质一栏

#include<iostream>
#include<cstdio>
using namespace std;

const int MAXN = 100000+9;
const int MX = 70000;

int pri[MAXN],tot,phi[MAXN];
bool tag[MAXN];

inline void Get_Phi(){
	phi[1] = 1;
	for (int i=2;i<=MX;i++) {
		if (!tag[i]) pri[++tot] = i, phi[i] = i-1;
		for (int j=1;j<=tot && pri[j]*i <= MX;j++) {
			tag[pri[j]*i] = 1;
			if (i % pri[j]) phi[pri[j]*i] = phi[i]*(pri[j]-1);
			else {phi[pri[j]*i] = phi[i]*pri[j]; break;}
		}
	}
}

int main(){
	Get_Phi(); int a;
	while (~scanf("%d",&a))	printf("%d\n",phi[a-1]);	
	return 0;
} 

我一定不会告诉你:其实我只是来水线性递推欧拉函数的QAQ

【BZOJ 3239】Discrete Logging

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3239
离线版题目:http://paste.ubuntu.com/19379662/

BSGS 的板题,然而把MAXINT记成2e10去了,调试了两个小时QAQ
BSGS 见算法总结,这么晚了,今天先扔一份代码就跑了吧QAQ

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;

const int MAXN = 9999971; 

int p,a,b;

namespace Hash{
	const int N = 10000000;
	const int MOD = 9999971;
	const int INF = 2000000000;
	int T,nxt[N/2],v1[N/2],head[N],v2[N/2];
	
	inline void insert(int w, int num){
		v1[++T] = w; v2[T] = num;
		nxt[T] = head[w%MOD]; head[w%MOD] = T;
	}
	
	inline void init(){
		memset(head,0,sizeof(head));
		T = 0;
	}
	
	inline int query(int w){
		int ret = INF;
		for (int i=head[w%MOD];i;i=nxt[i])
			if (v1[i] == w) ret = min(ret, v2[i]);
		if (ret != INF) return ret;
		else return -1;
	}
};

inline int quick_pow(int w, int t){
	int ret = 1;
	while (t) {
		if (t & 1) ret = ((LL)ret*w) % p;
		w = (LL)w*w % p; t >>= 1;
	}
	return ret;
}

inline void BSGS(){
	Hash::init(); 
	if (b == 1) printf("0\n");
	else {
 		int w = a, lim = ceil(sqrt(p));
 		for (int i=1;i<=lim;i++) {
			if (w == b) {printf("%d\n",i); return;} 
			Hash::insert(w,i); w = (LL)w*a%p;
		}
		a = w = quick_pow(a,lim);
		for (int i=1;i<=lim;i++) {
			int tmp = Hash::query((LL)b*quick_pow(w,p-2)%p);
			if (tmp > -1) {printf("%d\n",lim*i+tmp); return;}
			w = (LL)w*a%p;
		}
		printf("no solution\n");
	}
}

int main(){
	while (~scanf("%d%d%d",&p,&a,&b)) BSGS();
	return 0;
} 

【NOI五连测】[D2T1] 啊

题目传送门:http://pan.baidu.com/s/1hsLh92W
OJ传送门:http://oi.cdshishi.net:8080/Problem/1712

这一道题,我一眼看到的时候,也是“啊”的。博弈论?不会啊QAQ
最后只能打了暴力,结果还打错了QAQ

来说一说正解。
我们定义三种节点:
1.无名必胜
2.有名必胜
3.先手必胜

再来讨论一下这三种节点间的关系:
假如一个节点的儿子中,1、2两种类型的节点一样多,则为类型3
否则,1、2哪种多,该节点就是那种节点

于是搞一个树形DP,就可以搞出根节点的类型
显然,如果是类型3,则无解
如果是类型2,则只有部分节点可选
如果是类型1,则全部的叶子结点都可选

另外,本题还要注意,因为非叶子结点的颜色,最终会取决于它儿子节点的颜色
所以,第一步走的节点,一定是叶子结点,否则,并没有什么卵用QAQ

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

const int MAXN = 100000+9;

int n,vout,que[MAXN],type[MAXN],num[MAXN][3];
vector<int> G[MAXN];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline void init(){
	n = read();
	for (int i=1;i<=n;i++) G[i].clear(), num[i][0] = num[i][1] = num[i][2] = 0;
	for (int i=1;i<=n;i++) G[read()].push_back(i);
	for (int i=1;i<=n;i++) type[i] = read() + 1;
	
	int fro,bak; que[fro=bak=1] = 1;
	while (fro < n) {
		int w = que[bak++];
		for (int i=0;i<(int)G[w].size();i++)
			que[++fro] = G[w][i];
	} 
}

void GetAns(int w){
	if ((int)G[w].size() == 0) {
		if (type[w] == 0) que[++vout] = w;
	} else {
		int t = num[w][2] - num[w][1];
		if (t <= 1 && t >= 0) for (int i=0;i<(int)G[w].size();i++)
			GetAns(G[w][i]);
	}
}

int main(){
	freopen("ah.in","r",stdin);
	freopen("ah.out","w",stdout);
	int T; cin>>T;
	while (T--) {
		init();
		for (int k=n,i=que[k];k;i=que[--k]){
			if ((int)G[i].size() == 0) continue;
			else { 
				for (int j=0;j<(int)G[i].size();j++) num[i][type[G[i][j]]]++;
				if (num[i][2] == num[i][1]) type[i] = 0;
				else if (num[i][1] > num[i][2]) type[i] = 1;
				else type[i] = 2;
			}
		} if (type[1] == 1) {
			int tot = 0; for (int i=1;i<=n;i++) if ((int)G[i].size() == 0 && type[i] == 0) que[++tot] = i;
			printf("%d ",tot); for (int i=1;i<=tot;i++) printf("%d ",que[i]); cout<<endl;
		} else if (type[1] == 0) {vout = 0, GetAns(1); sort(que+1,que+1+vout); cout<<vout<<' '; for (int i=1;i<=vout;i++) cout<<que[i]<<' '; cout<<endl;}  
		else printf("-1\n"); 
	}
	return 0;
} 

【BZOJ 1923】[Sdoi2010] 外星千足虫

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1923
数据生成器:http://paste.ubuntu.com/18996088/

今天在做高斯消元的专题。一看这题,欸,这不高斯消元求解异或方程组的板题吗? (~ ̄▽ ̄)→))* ̄▽ ̄*)o
于是写了一写,果然是板题。然而我想只有我这种纸张才会因为把结果输反了而wa了半天吧?QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;

const int MAXN = 1000+9;

int n,m,mat[MAXN][MAXN],tmp[MAXN],cnt;
char pat[MAXN],tag[MAXN];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline bool update(){
	for (int i=1;i<=n;i++) if (tmp[i]){
		if (!tag[i]) {
			tag[i] = 1; cnt++;
			for (int j=1;j<=n+1;j++) 
				mat[j][i] = tmp[j];
			return cnt == n;
		} else for (int j=1;j<=n+1;j++) tmp[j] ^= mat[j][i];
	}
	return cnt == n;
}

inline void output(int k){
	printf("%d\n",k);
	for (int i=n;i;i--){
		int ans = mat[n+1][i];
		for (int j=i+1;j<=n;j++)
			ans ^= mat[j][i];
		if (ans) tag[i] = 1;
		else tag[i] = 0;
		for (int j=i-1;j;j--)
			mat[i][j] *= ans;	
	}
	for (int i=1;i<=n;i++) 
		if (tag[i]) puts("?y7M#");
		else puts("Earth");
}

int main(){
	scanf("%d%d",&n,&m);
	for (int k=1;k<=m;k++){
		scanf("%s",pat+1); tmp[n+1]=read();
		for (int i=1;i<=n;i++)
			tmp[i] = pat[i]-'0';
		if (update()) output(k), exit(0);
	}
	puts("Cannot Determine\n");
	return 0;
} 

PS:这份代码是在线的做法,看题解,貌似离线也是可以做的。