zoukankan      html  css  js  c++  java
  • 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)相乘。

    一些例题

    之后再写吧,咕咕咕

  • 相关阅读:
    hdu1238 Substrings
    CCF试题:高速公路(Targin)
    hdu 1269 迷宫城堡(Targin算法)
    hdu 1253 胜利大逃亡
    NYOJ 55 懒省事的小明
    HDU 1024 Max Sum Plus Plus
    HDU 1087 Super Jumping! Jumping! Jumping!
    HDU 1257 最少拦截系统
    HDU 1069 Monkey and Banana
    HDU 1104 Remainder
  • 原文地址:https://www.cnblogs.com/oiertkj/p/12203495.html
Copyright © 2011-2022 走看看