• 2019年5,6月博客汇总


    [USACO12FEB]牛的IDCow IDs

    显然用的位数越多能表示出的数就越多,在n个位数中选择m个位数为1的方案数明显为(C_n^m)。我们从最高位向下进行考虑,我们可以从小往大枚举选择的位数。如过(C_i^k)大于n且(C_{i-1}^k)时,显然在k-1位怎么放都无法满足要求,所以最高位为i。我们依次向下确定,则接下来要放置k-1位的第(n-C_{i-1}^k)位。依次类推即可。(中间可以二分以降低复杂度,但是暴力就能过)

    这题预处理C会比较方便

    代码

    #include<cstdio>
    using namespace std;
    namespace orz{
    const int N=100005;
    const long long MAX=100000000;
    long long C[N][12];
    int ans[12]; 
    inline int read(){
        int a=1,b=0;char t;
        do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
        do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
        return a*b;
    }
    inline void showC(){
    	for(int i=0;i<=20;++i){
    		for(int j=0;j<=12;++j){
    			if(!C[i][j]){
    				putchar('
    ');
    				break;
    			}
    			printf("%lld ",C[i][j]);
    		}
    	}	
    }
    int QAQ(){
    	freopen("3048.in","r",stdin);
    	int n,k;
    	n=read();k=read();
    	if(k==1){
    		putchar('1');
    		for(int i=1;i<n;++i)
    			putchar('0');
    		return false;
    	}
    	C[0][0]=1;
    	for(int i=1,j=1;i<N;++i){
    		C[i][0]=1;
    		for(j=1;j<=k;++j){
    			C[i][j]=C[i-1][j]+C[i-1][j-1];
    			if(C[i][j]>MAX||C[i][j]==0)break;
    		}
    		if(C[i][j]>MAX||C[i][j]==0)continue;
    	}
    	for(int i=k;i;--i){
    		for(int j=1;;++j){
    			if(C[j][i]>=n){//如果放置数大于n 
    				ans[i]=j;
    				n-=C[j-1][i];
    				break;
    			}
    		}
    	}
    	for(int i=ans[k],cnt=k;i;--i){
    		if(i==ans[cnt]){
    			putchar('1');
    			--cnt;
    		}
    		else{
    			putchar('0');
    		}
    	}
    	return false;
    }
    }
    int main(){
    	return orz::QAQ();
    }
    
    

    2019普通高等OIer全团队统一考试

    movie


    首先这个游戏是绝对公平的,所以它的概率和id并没有什么关系。
    所以就是(frac{k}{n})

    代码

    #include<cstdio>
    using namespace std;
    namespace orz{
    inline long long read(){
    	long long a=1,b=0;char t;
    	do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
    	do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
    	return a*b;
    }
    inline long long gcd(long long a,long long b){
    	return b?gcd(b,a%b):a;
    }
    int QAQ(){
    	long long n,k,id;
    	n=read(),k=read(),id=read();
    	if(n<=k){
    		printf("1/1");
    		return false;
    	}
    	if(k==0){
    		printf("0/1");
    		return false;
    	}
    	printf("%lld/%lld",k/gcd(k,n),n/gcd(k,n));
    	return false;
    }
    }
    int main(){
    	return orz::QAQ();
    }
    

    tower



    乍一看是一个数字三角形,实际上就是一个数字三角形。
    我们从上和下分别跑一次dp两个数值加起来减去当前值就可以得到必须经过当前点的答案。
    对于每一个询问,我们只需要查询除了这个点之外其他点答案中的最大值就可以了。

    那怎么查呢?
    如果暴力,复杂度会很高,然而G老师就这么过了
    我们可以建一棵线段树,每次查询是logn的。
    我们仔细考虑发现除了最大值那个点的答案是次大值,其他点的答案都是最大值。
    所以我们求出最大值次大值就可以了。

    线段树代码

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    namespace orz{
    const int N=510500;
    inline int read(){
    	int a=1,b=0;char t;
    	do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
    	do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
    	return a*b;
    }
    struct SegmentTree{
    	int l,r,mx;
    }t[(N<<2)+1];
    int s[N],top[N],bottom[N],c[N];
    int n,m;
    inline int num(int x,int y){
    	return (x-1)*x/2+y;
    }
    int query(int p,int l,int r){
    	if(t[p].l==l&&t[p].r==r)return t[p].mx;
    	int mid=(t[p].l+t[p].r)>>1;
    	if(r<=mid)return query(p<<1,l,r);
    	else if(l>mid)return query(p<<1|1,l,r);
    	else return max(query(p<<1,l,mid),query(p<<1|1,mid+1,r));
    }
    inline int ask(int x,int y){
    	if(x==1&&y==1)return -1;
    	int ans=-100000;
    	if(y^1)ans=max(ans,query(1,num(x,1),num(x,y-1)));
    	if(y^x)ans=max(ans,query(1,num(x,y+1),num(x,x)));
    	return ans;
    }
    void build(int p,int l,int r){
    	t[p].l=l;t[p].r=r;
    	if(l==r){
    		t[p].mx=top[l]+bottom[l]-s[l];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(p<<1,l,mid);
    	build(p<<1|1,mid+1,r);
    	t[p].mx=max(t[p<<1].mx,t[p<<1|1].mx);
    }
    inline void dp(){
    	top[num(1,1)]=s[num(1,1)];
    	for(int i=2;i<=n;++i)
    		top[num(i,1)]=top[num(i-1,1)]+s[num(i,1)],
    		top[num(i,i)]=top[num(i-1,i-1)]+s[num(i,i)];
    	for(int i=2;i<=n;++i)
    		for(int j=2;j<i;++j)
    			top[num(i,j)]=max(top[num(i-1,j)],top[num(i-1,j-1)])+s[num(i,j)];
    	for(int i=1;i<=n;++i)
    		bottom[num(n,i)]=s[num(n,i)];
    	for(int i=n-1;i;--i)
    		for(int j=1;j<=i;++j)
    			bottom[num(i,j)]=max(bottom[num(i+1,j)],bottom[num(i+1,j+1)])+s[num(i,j)];
    	build(1,1,num(n,n));
    }
    int QAQ(){
    //		freopen("tower.in","r",stdin);
    	int x,y;
    	n=read(),m=read();
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=i;++j)
    			s[num(i,j)]=read();
    	dp();
    	while(m--){
    		x=read(),y=read();
    		printf("%d
    ",ask(x,y));
    	}
    	return false;
    }
    }
    int main(){
    	return orz::QAQ();
    }
    

    正解代码

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    namespace orz{
    const int N=510500;
    inline int read(){
    	int a=1,b=0;char t;
    	do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
    	do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
    	return a*b;
    }
    int s[N],top[N],bottom[N],ans[N],c[N];
    int mx[1002];
    int n,m;
    inline int num(int x,int y){
    	return (x-1)*x/2+y;
    }
    inline void dp(){
    	top[num(1,1)]=s[num(1,1)];
    	for(int i=2;i<=n;++i)
    		top[num(i,1)]=top[num(i-1,1)]+s[num(i,1)],
    		top[num(i,i)]=top[num(i-1,i-1)]+s[num(i,i)];
    	for(int i=2;i<=n;++i)
    		for(int j=2;j<i;++j)
    			top[num(i,j)]=max(top[num(i-1,j)],top[num(i-1,j-1)])+s[num(i,j)];
    	for(int i=1;i<=n;++i)
    		bottom[num(n,i)]=s[num(n,i)];
    	for(int i=n-1;i;--i)
    		for(int j=1;j<=i;++j)
    			bottom[num(i,j)]=max(bottom[num(i+1,j)],bottom[num(i+1,j+1)])+s[num(i,j)];
    	for(int i=1;i<=num(n,n);++i)
    		c[i]=top[i]+bottom[i]-s[i];
    	for(int i=1;i<=n;++i){
    		int t=-1;
    		for(int j=1;j<=i;++j)
    			if(c[num(i,j)]>t){
    				t=c[num(i,j)];
    				mx[i]=j;
    			}
    	}
    	for(int i=1;i<=n;++i){
    		int t=-1;
    		for(int j=1;j<=i;++j)
    			if(j!=mx[i]){
    				t=max(t,c[num(i,j)]);
    				ans[num(i,j)]=c[num(i,mx[i])];
    			}
    		ans[num(i,mx[i])]=t;
    	}
    }
    int QAQ(){
    //	freopen("tower.in","r",stdin);
    	int x,y;
    	n=read(),m=read();
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=i;++j)
    			s[num(i,j)]=read();
    	dp();
    	while(m--){
    		x=read(),y=read();
    		printf("%d
    ",ans[num(x,y)]);
    	}
    	return false;
    }
    }
    int main(){
    	return orz::QAQ();
    }
    

    segmenttree




    直接暴力,得分50。    

    动态开点的暴力,得分75。    

    说实话这题暴力分有点多。
    线段树也是一棵树,所以我们应该从树的角度来考虑。
    对于一个区间,它最差的区间定位是每一个叶子一个区间。
    然而如果有一个爸爸的话答案就会减少1。设这个区间一共有t个完全被包含的区间。
    长度为k。
    于是有t-k个爸爸。
    所以答案为(k-(t-k)=2k-t)

    代码

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define IT vector<ques>::iterator
    using namespace std;
    namespace orz{
    const int N=500000;
    int c[N];
    int n,m;
    int ans[N],len[N];
    int tot=0;
    struct node{
    	int l,r;
    	bool operator<(const node &a)const{
    		return this->l<a.l;
    	}
    }a[N];
    struct ques{
    	int l,r,t,id;
    	ques(int a,int b,int c,int d){
    		l=a,r=b,t=c,id=d;
    	}
    };
    vector<ques>q[N];
    inline int read(){
    	int a=1,b=0;char t;
    	do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
    	do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
    	return a*b;
    }
    inline void add(int x,int k){
    	for(;x<=n;x+=x&-x)
    		c[x]+=k;
    }
    inline int ask(int x){
    	int ans=0;
    	for(;x;x-=x&-x)
    		ans+=c[x];
    	return ans;
    }
    void build(int l,int r){
    	a[++tot].l=l,a[tot].r=r;
    	if(l==r)return ;
    	int mid=read();
    	build(l,mid),build(mid+1,r);
    }
    int QAQ(){
    //		freopen("segment.in","r",stdin);
    	n=read(),m=read();
    	int x,y;
    	build(1,n);
    	sort(a+1,a+tot+1);
    	for(int i=1;i<=m;++i){
    		x=read(),y=read();
    		len[i]=y-x+1;
    		q[x-1].push_back(ques(x,y,-1,i));
    		q[y].push_back(ques(x,y,1,i));
    	}
    	int cwy=1;
    	for(int i=1;i<=n;++i){
    		while(a[cwy].l==i){
    			add(a[cwy].r,1);
    			++cwy;
    		}
    		for(IT j=q[i].begin();j!=q[i].end();++j)
    			ans[j->id]+=(ask(j->r)-ask(j->l-1))*j->t;
    	}
    	for(int i=1;i<=m;++i)
    		printf("%d
    ",len[i]*2-ans[i]);
    	return false;
    }
    }
    int main(){
    	return orz::QAQ();
    }
    

    20190610爆零赛

    爆零了QAQ,自闭了。

    math


    我们看到((-1)^n)的形式可以想到和奇偶性有关,我们考虑约数个数的公式((c_1+1)(c_2+1)(c_3+1)...(c_n+1))。我们会发现其中只要有一个数是偶数整个式子就是偶数,我们考虑它的值为奇数的情况,则每一个(c_i)都为偶数,即这个数为一个完全平方数。
    所以问题也就变为了对于每一个i,有多少个(ij)为完全平方数。
    对于一个数来说我们先把它所有指数为奇数的项补为偶数,这显然是它所能乘出的第一个平方数。然后我们对它成对的补指数一定也是个平方数。写到一半开开告诉我这么推不出来。
    我认真研读了题解。
    正解思路和这个类似,我们把这个数的每一个指数为奇数的项提出一个来,这个数i可以表示为(p*q^2_1),满足条件的j可以表示为(p*q^2_2),所以答案就为(sqrt{frac{m}{p}})向下取整,相当于先把p选出来后在去数q。
    所以我们只要把每一个i的p搞出来就可以了。
    我们来线性筛,记录一个ans数组,在线性筛中我们把这个数用i×prime[j]表示,我们判断ans[i]里面有没有prime[j]就可以了。
    这次各种题都卡常233。

    代码

    #include<cstdio>
    #include<cmath>
    using namespace std;
    namespace orz{
    const int N=10000100;
    inline long long read(){
        long long a=1,b=0;char t;
        do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
        do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
        return a*b;
    }
    int mindiv[N],prime[N],ans[N],tot=0;
    int QAQ(){
        freopen("math.in","r",stdin);
        freopen("math.out","w",stdout);
        int n=read();
        long long m=read();
        register int k;
        int question=0;
        ans[1]=1;
        register int j;
        for(register int i=2;i<=n;++i){
            if(!mindiv[i])prime[++tot]=mindiv[i]=ans[i]=i;
            for(j=1;j<=tot&&((k=i*prime[j])<=n)&&prime[j]<=mindiv[i];++j){
                mindiv[k]=prime[j];
                if(ans[i]%prime[j]==0)ans[k]=ans[i]/prime[j];
                else ans[k]=ans[i]*prime[j];
            }
        }
        for(register int i=1;i<=n;++i)
            question+=(int)sqrt(m/ans[i])&1?-1:1;
        printf("%d",question);
        return false;
    }
    }
    int main(){
        return orz::QAQ();
    }
    

    osu


    这题乍一看是个DP,直接DP的话是个(O(n^3))的大暴力,所以肯定是过不了的,蒟蒻因为写的太丑连暴力分都没拿够。
    我们考虑二分,虽然原题中值的形式比较诡异,但是总共就(n^2)个边权,都跑出来二分也可以。二分之后跑个最长路来check。
    因为原图是个DAG,所以最长路可以dp来求,最终复杂度为(O(n^2logn^2))

    代码

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    namespace orz{
    const int N=2022;
    int cnt=0;
    int id[N][N];
    int n,k;
    int sx[((N*N)>>1)+1000];
    inline int read(){
        int a=1,b=0;char t;
        do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
        do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
        return a*b;
    }
    struct num{
        long long a,b,c;
        inline long long gcd(int a,int b){
            return b?gcd(b,a%b):a;	
        }
        bool operator<(const num&b)const{
            return this->a*this->a*this->b*b.c*b.c<b.a*b.a*b.b*this->c*this->c;
        }
        void print(void){
            int t;
            t=sqrt(b);
            for(int i=t;i;--i){
                if(b%(i*i)==0){
                    b/=i*i;
                    a*=i;
                }
            }
            t=gcd(a,c);
            a/=t;
            c/=t;
            printf("%lld %lld %lld",a,b,c);
        }
    };
    struct node{
        int id;
        num v;
        bool operator< (const node &a)const {
            return this->v<a.v;
        }
    }b[((N*N)>>1)+1000];
    struct point{
        int x,y,t;
    }a[N];
    inline num getdis(int x,int y){
        num res;
        res.b=(a[x].x-a[y].x)*(a[x].x-a[y].x)+(a[x].y-a[y].y)*(a[x].y-a[y].y);
        res.c=a[y].t-a[x].t;
        res.a=1;
        return res;
    }
    int f[N];
    inline bool check(int x){
        for(int i=1;i<=n;++i)
            f[i]=0;
        for(int i=0;i<=n;++i)
            for(int j=i+1;j<=n;++j)
                if(sx[id[i][j]]<=x){
                    f[j]=max(f[j],f[i]+1);
                    if(f[j]>=k)return true;
                }
        return false;
    }
    int QAQ(){
        freopen("osu.in","r",stdin);
        freopen("osu.out","w",stdout);
        n=read(),k=read();
        for(int i=1;i<=n;++i)
            a[i].t=read(),a[i].x=read(),a[i].y=read();
        for(int i=0;i<=n;++i)
            for(int j=i+1;j<=n;++j)
                id[i][j]=++cnt;
        for(int i=0;i<=n;++i)
            for(int j=i+1;j<=n;++j)
                b[id[i][j]].v=getdis(i,j),b[id[i][j]].id=id[i][j];
        sort(b+1,b+cnt+1);
        for(int i=1;i<=cnt;++i)
            sx[b[i].id]=i;
        register int l=1,r=cnt,mid;
        while(l<r){
            mid=(l+r)>>1;
            if(check(mid))r=mid;
            else l=mid+1;
        }
        b[l].v.print();
        return false;
    }
    }
    int main(){
        return orz::QAQ();
    }
    

    map


    这回考试的难度是单调递减的,最后一题比较水。首先一眼就可以看出来这是个Tarjan题,在一个边双里的点一定是安全点对,边双缩点后原图变成一棵树,点权为对应原图的点数。在加了一条边后对树上的一条链都有影响,我们设这条链上点的总数为sum,一个点上的点数为(a_i),对于每一个点,它对答案的贡献都是(a_i(sum-a_i)=a_isum-a_i^2)我们把它们求一个和,变成了(sum^2-sum a_i^2)。我们维护两个数组,一个是根节点到这个点的点数和,一个是根节点到这个点的点数的平方和。求个LCA一减就出来了。复杂度(O(qlogn))

    代码

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    namespace orz{
    const int N=1001000;
    struct graph{
        int head[N],ver[N<<1],next[N<<1],tot;
        void add(int x,int y){
            next[++tot]=head[x],head[x]=tot,ver[tot]=y;
            next[++tot]=head[y],head[y]=tot,ver[tot]=x;
        }
    }a,b;
    int n;
    int dfn[N],low[N];
    int c[N],cut[N<<1];
    long long sum[N],mi[N];
    long long S[N],M[N];
    bool vis[N];
    int tol=0;
    int cnt=0;
    int top[N],son[N],fa[N],depth[N],size[N];
    inline int read(){
        int a=1,b=0;char t;
        do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
        do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
        return a*b;
    }
    void tarjan(int x,int edge){
        low[x]=dfn[x]=++tol;
        vis[x]=true;
        int y;
        for(int i=a.head[x];i;i=a.next[i]){
            if(i==(edge^1))continue;
            if(vis[y=a.ver[i]])low[x]=min(low[x],dfn[y]);
            else {
                tarjan(y,i),low[x]=min(low[x],low[y]);
                if(low[x]<low[y])cut[i]=cut[i^1]=true;
            }
        }
    }
    void BFS(){
        int x,y;
        for(int i=1;i<=n;++i)vis[i]=0;
        queue<int>q;
        for(int i=1;i<=n;++i){
            if(vis[i])continue;
            ++cnt;
            q.push(i);
            while(q.size()){
                x=q.front(),q.pop();
                if(vis[x])continue;
                vis[x]=true;
                c[x]=cnt;++sum[cnt];
                for(int i=a.head[x];i;i=a.next[i]){
                    if(cut[i])continue;
                    if(vis[y=a.ver[i]])continue;
                    q.push(y);
                }
            }
        }
    }
    void dky(int x,int father){
        size[x]=1;fa[x]=father;
        int mx=0;
        int y;
        for(int i=b.head[x];i;i=b.next[i]){
            if((y=b.ver[i])==father)continue;
            depth[y]=depth[x]+1;
            S[y]=S[x]+sum[y];
            M[y]=M[x]+mi[y];
            dky(y,x);
            size[x]+=size[y];
            if(size[y]>mx){
                mx=size[y];
                son[x]=y;
            }
        }
    }
    void dfs(int x,int topf){
        top[x]=topf;
        int y;
        if(son[x])dfs(son[x],topf);
        for(int i=b.head[x];i;i=b.next[i]){
            if((y=b.ver[i])==fa[x])continue;
            if(y==son[x])continue;
            dfs(y,y);
        }
    }
    inline int LCA(int x,int y){
        while(top[x]^top[y]){
            if(depth[top[x]]>depth[top[y]])x=fa[top[x]];
            else y=fa[top[y]];
        }
        return depth[x]<depth[y]?x:y;
    }
    inline long long solve(int x,int y){
        int t=LCA(c[x],c[y]);
        return (S[c[x]]+S[c[y]]-2*S[t]+sum[t])*(S[c[x]]+S[c[y]]-2*S[t]+sum[t])
            -(M[c[x]]+M[c[y]]-2*M[t]+mi[t]);
    }
    int QAQ(){
        freopen("map.in","r",stdin);
        freopen("map.out","w",stdout);
        int m,q;
        a.tot=b.tot=1;
        long long ans=0;
        n=read(),m=read(),q=read();
        for(int i=1;i<=m;++i)
            a.add(read(),read());
        tarjan(1,0);
        BFS();
        for(int i=2;i<=a.tot;i+=2){
            if(c[a.ver[i]]==c[a.ver[i^1]])continue;
            b.add(c[a.ver[i]],c[a.ver[i^1]]);
        }
        for(int i=1;i<=n;++i)
            mi[i]=sum[i]*sum[i];
        depth[1]=1;
        S[1]=sum[1];
        M[1]=mi[1];
        dky(1,0);
        dfs(1,1);
        while(q--)
            ans+=solve(read(),read());
        printf("%lld",ans);
        return false;
    }
    }
    int main(){
        return orz::QAQ();
    }
    

    [P2000]拯救世界

    写DP写累了来颓一会式子。
    我们为每一个物品建立一个生成函数,用于召唤两个大神的同名神石视作不同的物品。
    (G_1(x)=x^0+x^6+x^{12}+...=frac{1}{1-x^6})
    (G_2(x)=x^0+x^1+...+x^9=frac{1-x^{10}}{1-x})
    (G_3(x)=x^0+x^1+...+x^5=frac{1-x^6}{1-x})
    (G_4(x)=x^0+x^4+x^8+...=frac{1}{1-x^4})
    (G_5(x)=x^0+x^1+...+x^7=frac{1-x^8}{1-x})
    (G_6(x)=x^0+x^2+x^4+...=frac{1}{1-x^2})
    (G_7(x)=x^0+x^1=x+1=frac{1-x^2}{1-x})
    (G_8(x)=x^0+x^8+x^{16}+...=frac{1}{1-x^8})
    (G_9(x)=x^0+x^{10}+x^{20}+...=frac{1}{1-x^{10}})
    (G_{10}(x)=x^0+x^1+x^2+x^3=frac{1-x^4}{1-x})

    然后我们把这一大坨东西乘起来。
    (G(x)=(frac{1}{1-x^6})(frac{1-x^{10}}{1-x})(frac{1-x^6}{1-x})(frac{1}{1-x^4})(frac{1-x^8}{1-x})(frac{1}{1-x^2})(frac{1-x^2}{1-x})(frac{1}{1-x^8})(frac{1}{1-x^{10}})(frac{1-x^4}{1-x}))
    能约的约一下
    (G(x)=(frac{1}{1})(frac{1}{1-x})(frac{1}{1-x})(frac{1}{1})(frac{1}{1-x})(frac{1}{1})(frac{1}{1-x})(frac{1}{1})(frac{1}{1})(frac{1}{1-x}))
    (G(x)=frac{1}{(1-x)^5})

    我们有如下公式:
    (frac{1}{(1-x)^n}=sum_{i=0}^infin C_{n+i-1}^{i}x^i)
    带入得:
    (G(x)=sum_{i=0}^infin C_{i+4}^ix^i)
    所以所求答案为:(C_{i+4}^i=C_{i+4}^4=frac{P_{i+4}^4}{4!}=(n+1)(n+2)(n+3)(n+4)/24)
    因为数据非常大所以应该用时间复杂度为(O(nlog n))的高精乘。
    但是我不会。
    用py就AC了

    [P2762]太空飞行问题

    我们发现实验对答案的贡献是正的,而仪器对答案的贡献的是负的,所以我们可以先假设所有的实验都选了,选择一个实验意味着不做这个实验,而选择一个仪器意味着使用了这个仪器。
    所以我们要求最小的负贡献。
    所以源点向所有仪器连容量为仪器费用的边,仪器向对应的汇点连容量为无穷大的边,实验向汇点连容量为实验价值的边。

    我们求最小割。
    首先最小割中不会有无穷大的边。
    割掉实验后仪器就不一定要割
    割掉所有仪器后实验就可以不选
    这样符合题意,所以可以求最小割

    这一题的输入十分诡异,好心的出题人给我们了一种输入方法。
    关于最后的方案输出,最后还有depth的点就是选择的实验。
    这个好像是与最大权闭合子图有关,之后再写个具体的博客。

    #include<bits/stdc++.h>
    using namespace std;
    namespace orz{
    #define IT vector<int>::iterator
    const int N=100000,inf=1<<29;
    int head[N],ver[N<<1],next[N<<1],edge[N<<1],tot=1;
    int depth[N],maxflow;
    int cost[N];
    int value[N];
    int n,m;
    int s,t;
    bool shiyan[N];
    bool flag;
    bool yiqi[N];
    vector<int>equipment[N];
    inline bool BFS(){
    	int x,y;
    	for(int i=1;i<=t;++i)
    		depth[i]=0;
    	queue<int>q;
    	q.push(s);
    	depth[s]=1;
    	while(q.size()){
    		x=q.front(),q.pop();
    		for(int i=head[x];i;i=next[i]){
    			if(!depth[y=ver[i]]&&edge[i]){
    				depth[y]=depth[x]+1;
    				if(y==t)return true;
    				q.push(y);
    			}
    		}
    	}
    	return false;
    }
    inline int dinic(int x,int flow){
    	if(x==t)return flow;
    	int k,y,rest=flow;
    	for(int i=head[x];i&&rest;i=next[i]){
    		if(edge[i]&&depth[y=ver[i]]==depth[x]+1){
    			k=dinic(y,min(edge[i],rest));
    			depth[y]=k?depth[y]:0;
    			edge[i]-=k;
    			edge[i^1]+=k;
    			rest-=k;
    		}
    	}
    	return flow-rest;
    }
    inline void add(int x,int y,int z){
    	next[++tot]=head[x],head[x]=tot,ver[tot]=y,edge[tot]=z;
    	next[++tot]=head[y],head[y]=tot,ver[tot]=x,edge[tot]=0;
    }
    inline int read(){
    	int a=1,b=0;char t;
    	do{t=getchar();if(t=='-')a=-1;}while(t>'9'||t<'0');
    	do{b=b*10-'0'+t;t=getchar();}while(t>='0'&&t<='9');
    	if(t=='
    ')flag=false;
    	return a*b;
    }
    char tools[10000];
    int QAQ(){
    //	freopen("qwq.in","r",stdin);
    	n=read(),m=read();
    	int sum=0;
    	s=n+m+1;
    	t=s+1;
    	for(int i=1;i<=n;++i){
    		sum+=(value[i]=read());
    		memset(tools,0,sizeof tools);
    		cin.getline(tools,10000);
    		int ulen=0,tool;
    		while (sscanf(tools+ulen,"%d",&tool)==1)//之前已经用scanf读完了赞助商同意支付该实验的费用
    		{   //tool是该实验所需仪器的其中一个      
    			//这一行,你可以将读进来的编号进行储存、处理,如连边。
    			equipment[i].push_back(tool);
    			if (tool==0) 
    				ulen++;
    			else {
    				while (tool) {
    					tool/=10;
    					ulen++;
    				}
    			}
    			ulen++;
    		}
    	}
    	for(int i=1;i<=m;++i)
    		cost[i]=read();
    	for(int i=1;i<=n;++i){
    		add(s,i,value[i]);
    		for(IT j=equipment[i].begin();j!=equipment[i].end();++j)
    			add(i,*j+n,inf);
    	}
    	for(int i=1;i<=m;++i)
    		add(i+n,t,cost[i]);
    	while(BFS())
    		maxflow+=dinic(s,inf);
    	for(int i=1;i<=n;++i){
    		if(depth[i])
    			shiyan[i]=true;
    	}
    	for(int i=1;i<=n;++i)
    		if(shiyan[i]){
    			printf("%d ",i);
    			for(IT j=equipment[i].begin();j!=equipment[i].end();++j)
    				yiqi[*j]=true;
    		}
    	putchar('
    ');
    	for(int i=1;i<=m;++i)
    		if(yiqi[i])
    			printf("%d ",i);
    	putchar('
    ');
    	printf("%d",sum-maxflow);
    	return false;
    }
    }
    int main(){
    	return orz::QAQ();
    }
    

    懵逼钨丝繁衍学习笔记

    我懵逼了QAQ

    前言

    因为这篇和数论关系比较大,文中所有的(perp)表示互质,(a,b)表示gcd

    数论函数

    如果一个函数的定义域为正整数,值域为复数,那么将它称为数论函数。

    积性函数

    如果数论函数(f(n))满足:对于互质(p,q)(f(pcdot q)=f(p)cdot f(q))
    这样的函数称为(数论)积性函数。
    如果没有互质的限制则称为完全积性函数。

    对于所有积性函数(f(1)=1)
    积性函数的积也是积性函数

    常见的积性函数:
    除数函数(sigma_k(n)):表示n的约数的k次幂和
    约数和函数(sigma_1(n)),或(sigma(n))
    约数个数函数(r(n)),一般也写为(d(n))
    欧拉函数(varphi(n))
    莫比乌斯函数(mu(n))
    以下几个为完全积性函数:
    元函数(e)当命题为真时返回1
    狄利克雷卷积单位函数(varepsilon(n)=(n==1?1:0))
    常函数(1(n)=1)
    单位函数(id(n)=n)

    欧拉函数

    之前讲过但我没怎么听懂
    欧拉函数的公式:
    (varphi(n)=nprod(1-frac{1}{p_i}))
    (varphi(p^n)=p^n-p^{n-1})
    下面那个式子相当于所有数减去不互质的个数,用下面的式子回带算数唯一分解的式子可以得到上面的式子。
    欧拉函数的重要式子:
    (sumlimits_{d|n}varphi(d)=n)
    感性理解:
    我们写出如下的几个分数,以12为例:
    (frac{1}{12},frac{2}{12},frac{3}{12},frac{4}{12},frac{5}{12},frac{6}{12},frac{7}{12},frac{8}{12},frac{9}{12},frac{10}{12},frac{11}{12},frac{12}{12})
    我们把它们化简得到:
    (frac{1}{12},frac{1}{6},frac{1}{4},frac{1}{3},frac{5}{12},frac{1}{2},frac{7}{12},frac{2}{3},frac{3}{4},frac{5}{6},frac{11}{12},frac{1}{1})
    我们会发现在分母上所有的12的因子都被枚举到,即所有的(d|n)
    而对于每个(d|n)所有与它互质的分子也被枚举到了,也就是说有(varphi(d))个。
    它们的数目明显是n
    比较理性的证明我不会
    这个式子可以暴力硬解一些问题,因为右面是n,所以所有的正整数都可以带这个式子。

    (sumlimits_{dperp n}d=frac{n imesvarphi(n)}{2})
    由欧几里得定理得:((d,n)=d(n-d,n))所以互质的数是成对出现的,做一个类似倒序相加的处理就可以的到这个结论。

    莫比乌斯函数

    [ mu(x)=left{ egin{aligned} 1 && (n=1)\ 0 && (n有平方因子) \ (-1)^k && (n=prodlimits_{i=1}^kp^i) end{aligned} ight. ]

    极其重要的结论:
    (sumlimits_{d|n}mu(d)=varepsilon(n))
    证明:
    对于n=1的情况显然成立。
    一个n的约数是由从n的唯一算数分解定理分解出的数中选几个数的到的,因为一个质因子只要选择了2个及以上的指数,它对答案的贡献就为0。
    所以我们把n的分解出的质因数的指数都忽略,从中选择一些质数来组成我们的(d)
    我们选择出有k个质因子的d的方案数为(C_n^k),因为加上偶数个的减去奇数个的,所以最后为0。
    这个式子也可以拿来ning干(解题)。

    狄利克雷卷积

    狄利克雷卷积是一种函数间的运算。
    (h(n)=sumlimits_{d|n}f(d)g(frac{n}{d}))
    h即为f与g运算后得到的新函数。
    看起来就像是一般卷积的更数论的形式。
    性质
    狄利克雷卷积有一些很好的性质

    1. 积性函数的狄利克雷卷积仍然满足积性
    2. 完全积性函数的狄利克雷卷积不一定满足完全积性
    3. Dirichlet卷积同时也具有交换律、分配律
    4. Dirichlet卷积运算存在单位元:(fcdotvarepsilon=varepsiloncdot f=f)

    性质4的简单证明:
    因为只有(d=n)的时候(varepsilon)才为1,所以它成立。

    筛法

    筛法可以筛各种积性函数以及一些奇怪的东西。

    埃拉托斯特尼筛

    简称埃氏筛
    复杂度为(O(nloglog n))复杂度十分接近线性,所以你甚至可以拿它卡过一些要用线性筛的题。

    筛素数

    for(int i=2;i<=n;++i)
        if(!vis[i])
            for(int j=i*i;j<=n;j+=i)
                vis[j]=true;
    

    筛欧拉函数

    void euler(){
        for(int i=1;i<=n;++i) phi[i]=i;
        for(int i=2;i<=n;++i)
            if(phi[i]==i)
                for(int j=i;j<=n;j+=i) //必须从i开始
                    phi[j]=phi[j]/i*(i-1);
    }
    

    这个相当于直接用公式算的,并没有用到积性。

    线性筛

    记录了每一个数的最小质因子,从而实现每一个数只有它的最小质因子筛到,实现了(O(n))
    线性筛素数

    inline void Prime(){
        register int k;
        for(int i=2;i<=n;++i){
            if(!mindiv[i])
                prime[++tot]=i,mindiv[i]=i,is_prime[i]=true;
            for(int j=1;j<=tot&&((k=i*prime[j]<=n)&&prime[j]<=mindiv[i];++j)
                mindiv[k]=prime[j];
        }
    }
    

    另一种写法

    for(int i=2;i<=n;++i){
        if(!vis[i]) p[++cnt]=i;
            for(int j=1;j<=cnt&&i*p[j]<=n;++j){
                vis[i*p[j]]=1;
            if(i%p[j]==0) break;
        }
    }
    

    这种写法没有记录mindiv数组

    线性筛欧拉函数

    inline void Phi(){
        phi[1]=1;
        for(int i=2;i<=n;++i){
            if(!vis[i])prime[++tot]=i,phi[i]=i-1;
            for(int j=1;j<=tot&&i*prime[j]<=n;++j){
                vis[i*prime[j]]=true;
                if(i%prime[j]==0){
                    phi[i*prime[j]]=phi[i]*prime[j];
                    break;
                }
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
    

    关于不互质的解释:
    因为(prime[j]是i imes prime[j])的最小质因子,所以(varphi[i])的式子中已经有了((1-frac{1}{prime[i]}))这一项,所以把(prime[j])乘到前面的系数中就可以了。

    线性筛莫比乌斯函数

    inline void Mu(){
        mu[1]=1;
        for(int i=2;i<=n;++i){
            if(!vis[i])prime[++tot]=i,mu[i]=1;
            for(int j=1;j<=tot&&i*prime[j]<=n;++j){
                vis[i*prime[j]]=true;
                if(i%prime[j]==0){
                    mu[i*prime[j]]=0;
                    break;
                }
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
    

    线性筛积性函数的总结
    考虑质数怎么办,筛到最小质因子怎么办,互质怎么办。
    不过要先知道它是一个积性函数,这一点经常可以从狄利克雷卷积得出。

    前置知识:和式的化简方法

    早上听得一脸懵逼,现在觉得说不定有必要去把《具体数学》看一下qaq
    这里说一下大概的思想,一个和式可以看成一个循环的形式,我们去枚举不同的东西进行一些操作。和式的化简一般是改变枚举顺序来让式子变得更可求。
    我们要注意到的是式子一定要是等价的。
    比如你将一个后枚举的东西提到了前面,你就要思考它在前面的什么状态下被枚举到了,进而得到改变后的式子。

    莫比乌斯反演

    回顾:
    (sumlimits_{d|n}mu(d)=varepsilon(n))
    (fcdotvarepsilon=f) (狄利克雷卷积)

    莫比乌斯反演的式子:
    (g(m)=sumlimits_{d|n}f(d)LeftarrowRightarrow f(n)=sumlimits_{d|n}g(d)mu(frac{n}{d}))
    弱推:
    (g=fcdot 1)
    (gcdotmu=fcdotmucdot 1)
    (mucdot 1=varepsilon)
    (gcdotmu=fcdotvarepsilon)
    (gcdotmu=f)
    (f=gcdotmu)

    强推:

    一:
    已知(g(n)=sumlimits_{d|n}f(d))
    推出(f(n)=sumlimits_{d|n}g(d)mu(frac{n}{d}))
    (sumlimits_{d|n}g(d)mu(frac{n}{d}))
    (=sumlimits_{d|n}g(frac{n}{d})mu(d))
    (=sumlimits_{d|n}mu(d)sumlimits_{d'|frac{n}{d}}f(d')) 带入式子
    我们想要把f提到前面来,因为d是n的因子,(frac{n}{d})是n的因子,d'是(frac{n}{d})的因子,所以d'是n的因子,我们在最前面枚举d',之后我们考虑对于每一个(f(d'))它会和那些(mu)相乘。

    一些例题

    之后再写吧,咕咕咕

  • 相关阅读:
    linux--->curl
    linux--->定时任务
    php--->无限级分类
    php--->自己封装的简易版mvc框架
    php--->单例模式封装mysql操作类
    Linux使用退格键时出现^H ^?解决方法
    错误日志
    linux下redis服务器安装使用 安装php的redis扩展 安装laravel下的redis
    php system()
    laravel redis的使用
  • 原文地址:https://www.cnblogs.com/oiertkj/p/12203495.html
Copyright © 2020-2023  润新知