[BZOJ1041 [HAOI2008]圆上的整点]数论、勾股数相关定理

【题目地址】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1041
【算法分析】
以下内容来自师傅AekdyCoin的blog以及口授
现在给定了一个方程a^2+b^2=c^2  (c已知)
这,就是勾股数。
然后勾股数有一个很霸气的定理。
1、a=m^2-n^2
2、b=2*m*n
3、c=m^2+n^2
4、gcd(n,m)=1。
5、gcd(a,b,c)=1。
同时满足这五条式的是一组勾股数,而且对于所有满足这五条式的(m,n)乘一个k(k>=1),即(km,kn)。就可以表示所有的勾股数,并且勾股数和三元对(m,n,k)一一对应。
换句话说,每个勾股数都只能表示为一个三元对(m,n,k)。

好了,现在回到这题的算法上。
我们枚举k^2(程序中是变量:div),但是k这里有一个小技巧,并不需要枚举1<=k^2<=r,
只要枚举1<=k^2<=sqrt(r)即可。因为我们可以保证拆成的两个乘数的大小顺序。
然后枚举了k^2以后,
问题转化成能不能找到一对二元对(m,n),满足:
1、gcd(m,n)=1
2、m^2+n^2==r/(k^2)
3、gcd(m^2-n^2,2*m*n,m^2+n^2)=1
然后这样暴力还是会很慢的,于是师傅就把第三个条件直接抽出来先判断。
我们可以从:1、3两式推出  r%4=1!
于是直接预先判断,里面那个复杂度为O(sqrt(r))的部分就很少会执行了!
然后算法甚至达到了接近O(sqrt(r))的复杂度。

【其它】
时间到了0MS。空间也没用什么。
膜拜AekdyCoin大神。。。
【CODE】
#include #include using namespace std;
int ans=0;

int gcd(int a,int b){return b?gcd(b,a%b):a;}

void solve(int r){
if (r==1 || r%4!=1) return;
int m,n;
for (n=1;n*n*2 m=(int)(sqrt(r-n*n)+1e-5);
if (m*m+n*n!=r) continue;
if (gcd(m,n)==1 && m>n) ans++;
}
}

int main(){
int r,div;
cin >> r;
ans=0;
for (div=1;div*div<=r;div++){
if (r%div) continue;
solve(r/div);
solve(div);
}
ans*=8;
ans+=4;
cout << ans << endl;
}

ZOJ比赛——2010.4.17

本次比赛由我和CZM一起参加。
先贴提交记录:
Run ID Submit Time Judge Status Problem ID Language Run Time(ms) Run Memory(KB) User Name 3470 2010-04-17 16:19:45 Accepted K C++ 130 220 CWJ&&CZM 2991 2010-04-17 15:35:34 Accepted J C++ 890 13752 CWJ&&CZM 1496 2010-04-17 13:46:15 Accepted E FPC 20 28 CWJ&&CZM 1188 2010-04-17 13:28:52 Wrong Answer E FPC 20 28 CWJ&&CZM 1085 2010-04-17 13:20:44 Accepted G C++ 0 176 CWJ&&CZM 596 2010-04-17 12:50:55 Accepted B C++ 0 176 CWJ&&CZM 463 2010-04-17 12:45:57 Accepted L C++ 0 176 CWJ&&CZM 311 2010-04-17 12:41:33 Accepted A C++ 0 176 CWJ&&CZM
首先一开场,CZM没在,于是我AC3道水题:A、B、L。
然后CZM上了,然后兵分两路,他做E,我做K。
然后他说E题十分水。
然后我看了一下K以后,发现和SGU122很像,但是又略有不同,然后想留着后面做算了。
于是我看到G题,一大堆英文和五行的,什么都没看懂。
绝望之际,随便交了个n/2,居然AC了!
。。。十分无语
然后CZM的E题WA了一次,然后还是AC了。
于是我开始YY J题。
一看。。。恩,感觉应该是以i划分状态,然后f[i,j,k]表示第i个完成,A处理器末时间为j,B处理器末时间为k的时候能否做到。当我看到我的f是布尔型的时候,觉得有些搞笑。。。于是换成f[i,j]表示第i个任务完成,A处理器末时间为j,B处理器末时间最小能是多少。
然后将sum猥琐优化一下,1Y,不过这个时间确实挺难看。。。
然后我们两个最后再来搞K题,讨论了半天,CZM觉得他的构造法是对的,我表示没听懂~
我想的是费用流,写了没几行,突然想起费用流不能有环。。。
于是我想用度数预处理一下暴力,CZM继续做他的构造法。
然后我的AC,他用小号交WA了。。。
我的做法是:选取出度最大的一个点做起点,然后DFS即可。
因为图是接近完全图,所以搜出来也很正常。。。
然后,我想暴力C题,最后没时间,比赛结束。
最后我们过了7题。
rank:22
排名围观地址:http://acm.zju.edu.cn/onlinejudge/showContestRankList.do?contestId=308

现在说说AC的两道不算太弱智的题目,现在贴贴简单的解题报告和程序:
J:F[i,j]表示完成第i个任务,A处理器末时间为j时,B处理器末时间最小为多少。然后转移即可。
K:由于每个点的出度+入度=n-1,于是把出度最大的那个当起点,暴力DFS即可。当然也可以DLX。
【CODE For J】
#include using namespace std;
const int N=105;
const int INF=100000000;
int Tc,n;
int a[N],b[N],tail[N];
int F[N][N*N];
int list[N][N*N*2];

void init(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
}

int solve(){
int i,j,k,sum=0,*f;
a[n+1]=0;
for (i=1;i<=n+1;i++){
sum+=a[i];
for (j=0;j<=sum;j++)
F[i][j]=INF;
}
F[1][0]=0;
sum=0;
for (i=1;i<=n;i++){
f=F[i+1];
for (j=0;j<=sum;j++)
if (F[i][j]!=INF){
f[j+a[i]]=min(f[j+a[i]],max(F[i][j],j));
f[max(j,F[i][j])]=min(f[max(j,F[i][j])],F[i][j]+b[i]);
}
sum+=a[i];
}
int ans=INF;
for (j=0;j<=sum;j++)
ans=min(ans,max(j,F[n+1][j]));
return ans;
}

int main(){
scanf("%d",&Tc);
for (int i=1;i<=Tc;i++){
init();
cout << solve() << endl;
}
}

【CODE For K】
#include #include #include const int N=105;
int n,S,step;
int din[N],dout[N],a[N][N],v[N],list[N];
bool flag;

void init(){
scanf("%d",&n);
memset(din,0,sizeof(din));
memset(dout,0,sizeof(dout));
memset(a,0,sizeof(a));
int i,j,x,y;
for (i=1;i<=n*(n-1)/2;i++){
scanf("%d%d",&x,&y);
a[x][y]=1;
din[y]++;
dout[x]++;
}
}

void dfs(int i){
v[i]=1;
step++;
list[step]=i;
if (step==n){
flag=true;
return;
}
for (int j=1;j<=n;j++)
if (a[i][j] && !v[j]){
dfs(j);
if (flag) return;
}
step–;
v[i]=0;
}

void work(){
if (n==1){
printf("1n");
return;
}
S=1;
for (int i=1;i<=n;i++)
if (dout[i]>dout[S]) S=i;
memset(v,0,sizeof(v));
flag=false;
step=0;
dfs(S);
if (!flag) printf("Impossiblen");
else
for (int i=1;i<=n;i++){
printf("%d",list[i]);
if (i!=n) printf(" ");
else printf("n");
}
}

int main(){
int Tc;
scanf("%d",&Tc);
for (int i=1;i<=Tc;i++){
init();
work();
}
}

[SSLOJ1366 正方矩阵]二分答案+排序、利用RMQ思想达到比较的O(1)

【题目大意】
在一个N*M 的字母矩阵中寻找一个正方子矩阵, 该正方矩阵在原矩阵中重复出现至少K次. 这些正方子矩阵可以出现局部的重叠,但不能是完全的重叠.
输入给定N,M,K和这个矩阵。
输出满足条件的最大的正方子矩阵的边长, 如果没有正方子矩阵能满足条件,输出0.
【算法分析】
显然答案满足单调性。二分答案。
然后怎么判断呢?
我们可以通过预处理边长为2^i的正方形的排名,然后利用这个排名,四分需要的正方形空间。
然后利用n+1进制表示出来,作为他的权值。显然,对于同一个矩阵,权值是一定的。
而对于不同的矩阵,权值是一定不同的。于是达到的O(1)的比较。
所以最终算法复杂度O(N^2 lg^2 N)

然后听说还有另外一种方法是用hash的?
【其他】
跑得比较慢= =。。。
【CODE】
#include #include #include #include #define ra rank[lg[len]]
using namespace std;
const int N=505;
typedef long long lld;
struct PT{int x,y;}a[N*N];
int m,n,appear,len,ns,tx,ty;
char s[N][N];
int lg[N],rank[14][N][N];
lld lltmp,res;

inline void input(){
scanf("%d%d%d",&m,&n,&appear);
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++){
char ch=getchar();
while (ch==’ ‘ || ch==’n’) ch=getchar();
s[i][j]=ch;
}
}

inline lld count (PT *TMP){
res=0;
int &x=TMP->x,&y=TMP->y;
ns=1< res=ra[x][y]*(n+1);
if (tx<=m) res+=ra[tx][y];
res*=n+1;
if (ty<=n) res+=ra[x][ty];
res*=n+1;
if (tx<=m && ty<=n) res+=ra[tx][ty];
return res;
}

inline int cmp0(const void *x,const void *y){
return s[((PT *)x)->x][((PT *)x)->y]-s[((PT *)y)->x][((PT *)y)->y];
}

inline int cmps(const void *X,const void *Y){
lltmp=count((PT *)X)-count((PT *)Y);
if (lltmp>0) return 1;
if (lltmp<0) return -1;
return 0;
}

inline void init(){
int i,j,tmp,step;
lg[1]=0;
for (i=2;i<=max(n,m);i++){
lg[i]=lg[i-1];
if (1<<(lg[i]+1)==i-1) lg[i]++;
}

for (tmp=0,i=1;i<=m;i++)
for (j=1;j<=n;j++){
a[++tmp].x=i;
a[tmp].y=j;
}
qsort(a+1,m*n,sizeof(PT),cmp0);
for (tmp=0,i=1;i<=m*n;i++){
if (i==1 || s[a[i].x][a[i].y]!=s[a[i-1].x][a[i-1].y]) tmp++;
rank[0][a[i].x][a[i].y]=tmp;
}

for (step=1;step<=lg[max(n,m)];step++){
len=1< for (tmp=0,i=1;i<=m;i++)
for (int j=1;j<=n;j++){
a[++tmp].x=i;
a[tmp].y=j;
}
qsort(a+1,m*n,sizeof(PT),cmps);
for (tmp=0,i=1;i<=m*n;i++){
if (i==1 || count(&a[i-1])!=count(&a[i])) tmp++;
rank[step][a[i].x][a[i].y]=tmp;
}
}
}

inline int cmp(const void *x,const void *y){
lltmp=count((PT *)x)-count((PT *)y);
if (lltmp>0) return 1;
if (lltmp<0) return -1;
return 0;
}

inline bool solve(int NowAns){
int i,j,tmp,nL;
len=NowAns;
for (tmp=0,i=1;i<=m;i++)
for (j=1;j<=n;j++){
a[++tmp].x=i;
a[tmp].y=j;
}
qsort(a+1,m*n,sizeof(PT),cmp);
for (nL=1,i=2;i<=m*n;i++){
if (count(&a[i])!=count(&a[i-1])) nL=1;
else nL++;
if (nL>=appear) return true;
}
return false;
}

int main(){
input();
init();
int l=0,r=min(n,m),mid;
while (l+1 mid=(l+r)/2;
if (solve(mid)) l=mid;
else r=mid-1;
}
if (solve(r)) l=r;
printf("%dn",l);
}

[SSLOJ1362 指纹]各种猥琐预处理+树状数组

【题目大意】
有N个程序,每个数据有4个测试项。
给出每个程序在这4个测试项里的排名,(排名保证取遍1..N的每个数),排名越前越牛X。
然后假设对于某一条程序,存在另外一条程序在4项中,有>=3项比它优,那么它就成为了SB程序。
问有多少个SB程序。
【算法分析】
先枚举地移走某一项,然后剩下的三项一起弄。(移走的那项暂时忽略,因为只需>=3个比它优)
这三项又可以按某一项排序,然后问题就变成了将给出N个二元对(Ai,Bi),然后对于每个二元对,判断有没有出现在它之前的,且满足Ai然后我们可以以Ai为key值作树状数组的下标,然后就可以在lgN的复杂度内维护Ai<=K的点中,Bi最小是多少。然后再标记一下,问题就可以解决了。
【CODE】
#include #include #include const int N=100005;
int n;
int a[N][5],d[N][5],Q[N];
bool bad[N];

inline int cmp(const void *x,const void *y){return ((int *)x)[0]-((int *)y)[0];}

void solve(){
memcpy(d,a,sizeof(int)*5*(n+1));
qsort(d+1,n,sizeof(int)*5,cmp);
memset(Q,50,sizeof(int)*(n+1));
int i,res,j;
for (i=1;i<=n;i++){
res=0x7FFFFFFF;
j=d[i][1]-1;
while (j){
res j-=j&-j;
}
if (res j=d[i][1];
while (j<=n){
Q[j] j+=j&-j;
}
}
}

int main(){
scanf("%d",&n);
int i,j,k,tmp;
for (i=1;i<=n;i++){
a[i][4]=i;
for (j=0;j<4;j++)
scanf("%d",&a[i][j]);
}
for (k=0;k<4;k++){
for (i=1;i<=n;i++){
tmp=a[i][k]; a[i][k]=a[i][3]; a[i][3]=tmp;
}
solve();
for (int i=1;i<=n;i++){
tmp=a[i][k]; a[i][k]=a[i][3]; a[i][3]=tmp;
}
}
int ans=0;
for (int i=1;i<=n;i++)
if (bad[i]) ans++;
printf("%dn",ans);
for (int i=1;i<=n;i++)
if (bad[i]) printf("%dn",i);
}

[BZOJ1132 [POI2008]Tro]利用极角排序去绝对值

【题目地址】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1132
【解题经历】
先YM LCC大牛。。。
表示一直不会极角排序,做凸包我都是用按Y坐标排,然后弄上壳和下壳的那种。。。
然后YY了比较久,感觉很难。。。。
于是去围观LCC大牛的代码,OH。。。原来这么简单。
【算法分析】
实际上我之前想的都陷入了一个误区,那就是原点是任意的,而LCC大牛的代码中是取左上角的那个点做原点,那么所有的点都会在同一象限,那么直接用叉积就可以判断了(因为角度都0<=α<=π/2,叉积不会出现分两边的问题)。
现在看这道题,假设枚举一个点k,那么ans+=SUM(abs(Xi*Yj-Yi*Xj)) (1<=i然后我们对于点i,所要加的面积应该是Xi*((Yi+1) + (Yi+2) +….+ (Yn))  -  Yi*((Xi+1) + (Xi+2) + … + (Xn))。
好吧,维护和就可以。
然后要注意的是,极角排序是选定了左上角那个点的,然后每次都去掉这个基准点就可以不断进行,且没有重复,使得最后剩下<3个点,就是结果了。
【其他】。。。时间排最后几位,表示不明真相,为啥LCC大牛这么快呢。。。55555
【CODE】
#include using namespace std;
typedef __int64 lld;
const int N=3005;
int n;
struct Point{lld x,y;}P[N];
lld ans=0;

void input(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%I64d%I64d",&P[i].x,&P[i].y);
}

bool cmp(Point a,Point b){return a.x*b.yvoid count(int n){
lld Sx=0,Sy=0,i,k=1,S;
for (i=1;i<=n;i++)
if (P[i].x swap(P[k],P[n]);
for (i=1;i P[i].x-=P[n].x;
P[i].y-=P[n].y;
}
sort(P+1,P+n,cmp);
for (i=1;i ans+=P[i].x*Sy-P[i].y*Sx;
Sx+=P[i].x;
Sy+=P[i].y;
}
}

int main(){
input();
for (int i=n;i>=1;i–) count(i);
if (ans%2==1) printf("%I64d.5n",ans/2);
else printf("%I64d.0n",ans/2);
}