再观BZOJ2219数论之神...

 

BZOJ2480: Spoj3105 Mod

 

BZOJ1420: Discrete Root && BZOJ1319

 

BSGS的严格根号算法

BZOJ1607: [Usaco2008 Dec]Patting Heads 轻拍牛头

BZOJ4205: 卡牌配对

题解:

首先暴力连边匹配肯定是不行的...

由于$200$以内的质数仅有$46$个,我们构造三类点$ab(p_1,p_2),ac(p_1,p_2),bc(p_1,p_2)$分别表示参数1能被$p_1$整除,参数2能被$p_2$整除,剩下的依此类推.

然后对于左侧的点向符合条件的中间点连边.

对于右侧的点从符合条件的中间点连边.

这样做事实上是边分组,很显然是正确的.

直接用dinic玩就行了.

代码:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
 
#define inf 0x3f3f3f3f
 
struct Solver{
    static const int V=100010;
    static const int E=3000010;
    int head[V],next[E],flow[E],end[E],ind,d[V];
    inline void reset(){
        memset(head,-1,sizeof head);
        ind=0;
    }
    inline void addedge(int a,int b,int f){
        int q=ind++;
        end[q]=b;
        next[q]=head[a];
        head[a]=q;
        flow[q]=f;
    }
    inline void make(int a,int b,int f){
        addedge(a,b,f);
        addedge(b,a,0);
    }
    inline bool bfs(int s,int t){
        static int q[V];
        int fr=0,ta=0;
        memset(d,-1,sizeof d);
        d[q[ta++]=s]=0;
        while(fr!=ta){
            int i=q[fr++];
            for(int j=head[i];j!=-1;j=next[j])
                if(flow[j]&&d[end[j]]==-1)
                    d[q[ta++]=end[j]]=d[i]+1;
        }
        return d[t]!=-1;
    }
    inline int dinic(int p,int t,int Maxflow){
        if(p==t)
            return Maxflow;
        int last=Maxflow;
        for(int j=head[p];j!=-1;j=next[j])
            if(flow[j]&&d[end[j]]==d[p]+1){
                int to=dinic(end[j],t,flow[j]>last?last:flow[j]);
                if(to){
                    flow[j]-=to;
                    flow[j^1]+=to;
                    if(!(last-=to))
                        return Maxflow;
                }
            }
        d[p]=-1;
        return Maxflow-last;
    }
    inline int Maxflow(int s,int t){
        int re=0;
        while(bfs(s,t))
            re+=dinic(s,t,inf);
        return re;
    }
}g;
 
#define N 210
int p[N],cnt;
bool np[N];
inline void pre(){
    int i,j;
    for(i=2;i<=200;++i){
        if(!np[i])
            p[++cnt]=i;
        for(j=1;j<=cnt&&p[j]*i<=200;++j){
            np[i*p[j]]=1;
            if(i%p[j]==0)
                break;
        }
    }
}
int a[51][51],b[51][51],c[51][51];
int x[2][30010],y[2][30010],z[2][30010];
 
vector<int>v[N];
int main(){
    pre();
    int n,m;
    cin>>n>>m;
    int i,j,k;
    for(i=1;i<=n;++i)
        scanf("%d%d%d",&x[0][i],&y[0][i],&z[0][i]);
    for(i=1;i<=m;++i)
        scanf("%d%d%d",&x[1][i],&y[1][i],&z[1][i]);
     
    for(i=1;i<=200;++i)
        for(j=1;j<=cnt;++j)
            if(i%p[j]==0)
                v[i].push_back(j);
     
    int id=n+m;
    for(i=1;i<=cnt;++i)
        for(j=1;j<=cnt;++j){
            a[i][j]=++id;
            b[i][j]=++id;
            c[i][j]=++id;
        }
     
    int s=0,t=++id;
     
    g.reset();
    for(i=1;i<=n;++i)
        g.make(s,i,1);
    for(i=1;i<=m;++i)
        g.make(n+i,t,1);
     
    int p,q;
    for(i=1;i<=n;++i){
        for(j=0;j<v[x[0][i]].size();++j)
            for(k=0;k<v[y[0][i]].size();++k){
                p=v[x[0][i]][j];
                q=v[y[0][i]][k];
                g.make(i,a[p][q],1);
            }
        for(j=0;j<v[x[0][i]].size();++j)
            for(k=0;k<v[z[0][i]].size();++k){
                p=v[x[0][i]][j];
                q=v[z[0][i]][k];
                g.make(i,b[p][q],1);
            }
        for(j=0;j<v[y[0][i]].size();++j)
            for(k=0;k<v[z[0][i]].size();++k){
                p=v[y[0][i]][j];
                q=v[z[0][i]][k];
                g.make(i,c[p][q],1);
            }
    }
    for(i=1;i<=m;++i){
        for(j=0;j<v[x[1][i]].size();++j)
            for(k=0;k<v[y[1][i]].size();++k){
                p=v[x[1][i]][j];
                q=v[y[1][i]][k];
                g.make(a[p][q],n+i,1);
            }
        for(j=0;j<v[x[1][i]].size();++j)
            for(k=0;k<v[z[1][i]].size();++k){
                p=v[x[1][i]][j];
                q=v[z[1][i]][k];
                g.make(b[p][q],n+i,1);
            }
        for(j=0;j<v[y[1][i]].size();++j)
            for(k=0;k<v[z[1][i]].size();++k){
                p=v[y[1][i]][j];
                q=v[z[1][i]][k];
                g.make(c[p][q],n+i,1);
            }
    }
     
    cout<<g.Maxflow(s,t)<<endl;
     
    return 0;
}

BZOJ3301: [USACO2011 Feb] Cow Line

题目大意:

(1)给定一个排列,问排列的序号.

(2)给定一个序号,要求构造出给定序号的排列.

题解:

首先要知道怎么得出排列的序号.

假如序列的长度为$n$,且第$i$个元素为$P_i$.

则序号为:$\sum_{i=1}^{n}(n-i)!\sum_{j=i+1}^{n}[P_i>P_j]$

这样的话可以$O(n^2)$算出排列的序号.

构造序列的话也是一个道理,我是暴力的,没管复杂度...

代码:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;

typedef unsigned long long llu;

int a[21],ans[21];
llu fac[21];
bool ok[21];
int main(){
	int n,Q,i,j;
	char s[10];
	cin>>n>>Q;
	llu x;
	for(fac[0]=i=1;i<=20;++i)
		fac[i]=fac[i-1]*i;
	int temp;
	while(Q--){
		scanf("%s",s);
		if(s[0]=='P'){
			cin>>x;
			--x;
			for(i=1;i<=n;++i)
				a[i]=x/fac[n-i],x-=a[i]*fac[n-i];
			memset(ok,1,sizeof ok);
			for(i=1;i<=n;++i){
				temp=0;
				for(j=1;j<=n;++j){
					if((temp+=ok[j])==a[i]+1)
						break;
				}
				ok[ans[i]=j]=0;
			}
			for(i=1;i<=n;++i){
				if(i!=1)
					putchar(' ');
				printf("%d",ans[i]);
			}
			puts("");
		}
		else{
			for(i=1;i<=n;++i)
				scanf("%d",&a[i]);
			for(x=0,i=1;i<=n;++i){
				for(temp=0,j=i+1;j<=n;++j)
					temp+=(a[i]>a[j]);
				x+=fac[n-i]*temp;
			}
			cout<<x+1<<endl;
		}
	}
	return 0;
}

BZOJ3884: 上帝与集合的正确用法 数论

首先我们知道欧拉定理:

\[a^{\phi(p)}=1(mod~p)((a,p)=1)\]

那么看起来这道题有点能做了.

可惜如果\(p\)是偶数又怎么办?

我们有\(cx%cy=c(x%y)\)这条神奇的性质.也很容易证明.

这样,我们就可以将问题递归处理了.

对于\(\phi\)函数的不断迭代是\(log\)级别的,然后我们每次\(O(\sqrt{n}logn)\)求\(\phi\).因此时间复杂度为\(O(\sqrt{n}logn)\).

还有另外一种不用讨论的方法:

有公式:\(a^x=a^{x\%\phi(p)+\phi(p)}(mod~p)(x>=\phi(p))\)

这样就能更加自然地递归下去了.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
inline int getphi(int x){
    int re=x,t=x;
    for(int i=2;i*i<=x;++i){
        if(t%i==0){
            re=(LL)re*(i-1)/i;
            while(t%i==0)t/=i;
        }
    }if(t!=1)re=(LL)re*(t-1)/t;
    return re;
}
 
inline int ksm(int x,int y,int p){
    int re=1,t=x;for(;y;y>>=1,t=(LL)t*t%p)if(y&1)re=(LL)re*t%p;return re;
}
inline int calc(int p){
    if(p==1)return 0;
    else{
        int phi=getphi(p),re=calc(phi);
        return ksm(2,re+phi,p);
    }
}
 
int main(){
    int T,p;scanf("%d",&T);
    while(T--){
        scanf("%d",&p);
        printf("%d\n",calc(p));
    }
    return 0;
}

BZOJ2219:数论之神 数论

BZOJ2956:模积和 数学

思路:

其实是一道简单题.关键是要注意到题目中的一个条件:\(i\not=j\)!!!

我们考虑用所有的情况减去\(i=j\)的情况.

所有的情况就很好求了.令\(f(n)=\sum_{i=1}^{n}n\%i\),则所有的情况为\(f(n)*{f(m)}\).

这玩意很容易分块求.

然后\(i=j\)的情况的贡献是:

\((n\%i)*(m\%i)=(n-i*\frac{n}{i})*(m-i*\frac{m}{i})=nm+i^2\frac{n}{i}\frac{m}{i}-n*i\frac{m}{i}-m*i\frac{n}{i}\)

这样发现分开之后都可以分块求.

于是就\(O(\sqrt{n})\)水过.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
 
static const int mod=19940417,rev2=(mod+1)>>1,rev6=3323403;
 
inline void mul(int&x,int y){x=(LL)x*y%mod;}
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
inline void dec(int&x,int y){x=x<y?x+mod-y:x-y;}
inline int calc(int n){//return sigma i^2
    int res=n;mul(res,(LL)(n+1)*(2*n+1)%mod),mul(res,rev6);return res;
}
inline int cal(int l,int r){
    int cl=calc(l-1),cr=calc(r);
    return cr<cl?cr+mod-cl:cr-cl;
}
 
inline int solve1(int n,int lim){//return sigma i*(n/i)
    int lst,t,res=0;
    for(int i=1;i<=lim;i=lst+1){
        lst=min(lim,n/(n/i));
        mul(t=n/i,(LL)(i+lst)*(lst-i+1)%mod),mul(t,rev2);
        inc(res,t);
    }return res;
}
inline int solve2(int n){//return sigma n%i
    return ((LL)n*n%mod+mod-solve1(n,n))%mod;
}
inline int solve3(int n,int m,int lim){//return sigma i^2(n/i)(m/i)
    int lst,t,res=0;
    for(int i=1;i<=lim;i=lst+1){
        lst=min(n/(n/i),m/(m/i)),lst=min(lst,lim);
        mul(t=(LL)(n/i)*(m/i)%mod,cal(i,lst));
        inc(res,t);
    }return res;
}
 
int main(){
    int n,m;scanf("%d%d",&n,&m);register int i,j;
    int res=(LL)solve2(n)*solve2(m)%mod;
    dec(res,(LL)min(n,m)*n%mod*m%mod);
    inc(res,(LL)solve1(n,min(n,m))*m%mod);
    inc(res,(LL)solve1(m,min(n,m))*n%mod);
    dec(res,solve3(n,m,min(n,m)));
    printf("%d",res);
    return 0;
}