zoukankan      html  css  js  c++  java
  • 「2017 Multi-University Training Contest 7」2017多校训练7

    1002 Build a tree(递归)

    题目链接 HDU6121 Build a tree

    有一棵n个点的有根树,标号为0到n-1,i号点的父亲是(lfloorfrac{i-1}{k} floor)号点,求所有子树大小的异或和。(1leq n,kleq10^{18})

    找出n所在的链,然后从根开始递归处理。
    k=1的时候特判,1异或到n,每4个异或起来为0。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll n,k;
    int t,D;
    ll f[64],siz[64],tot[64];
    void init(){
        D=1;
        for(ll c=n-1;c;c=(c-1)/k)f[D++]=c;
        reverse(f+1,f+D);
        siz[0]=tot[0]=1;
        for(ll i=1,j=k;i<=D;++i,j*=k){
            siz[i]=siz[i-1]+j;
            tot[i]=(k&1?tot[i-1]:0)^siz[i];
        }
    }
    ll solve(int d){
        if(d==D)return 1;
        ll l=f[d]-f[d-1]*k-1;
        ll r=k-l-1;
        ll ans=n;
        if(l&1)ans^=tot[D-d-1];
        if(r&1)ans^=tot[D-d-2];
        n-=siz[D-d-1]*l+siz[D-d-2]*r+1;
        return ans^solve(d+1);
    }
    int main(){
        scanf("%d",&t);
        while(t--){
            scanf("%lld%lld",&n,&k);
            if(k==1){
                ll ans=0;
                for(ll i=n/4*4;i<=n;++i)
                    ans^=i;
                printf("%lld
    ",ans);
                continue;
            }
            init();
            printf("%lld
    ",solve(1));
        }
        return 0;
    }
    
    

    1006 Free from square(状压DP)

    题目链接 HDU6125 Free from square

    不大于n的所有正整数中选出至少1个且至多k个使得乘积不包含平方因子,对(10^9+7)取模。(1leq n,kleq 500)

    因为500内全部质因子没法直接状压。所以只把小于根号n的质因子状压起来,只有8个。
    将大于根号n的质数划分为不同组,每组包含它们各自的倍数,每个组只能选1个数。且前8个质数每个在选出来的数的因子中最多出现一次。这是一次背包。
    1~n中不包含平方因子且不包含大于根号n的质因子的数,做01背包,也要求前8个质数都最多出现一次。

    f[i][j][k]表示前i组中选j个数,包含的质因子状态为k的方案数
    dp[i][j][k]表示前i个数选j个,包含的质因子状态为k的方案数

    再把两个背包合并起来。
    f计算成前缀和,即f[i][j][k]表示选不超过j个数的方案数。
    最后答案还要减去1,因为不能两个都选0个。
    第一维滚动。

    #include <bits/stdc++.h>
    #define ll long long
    #define mem(a,b) memset(a,b,sizeof(a))
    #define r(i,l,r,d) for(int i=(l);i<(r);i+=(d))
    #define rep(i,l,r) for(int i=(l);i<(r);++(i))
    #define add(x,y) x=(x+y)%M
    const ll M=1e9+7;
    const int N=515;
    using namespace std;
    int t,n,m;
    ll dp[2][N][N];
    ll f[2][N][N];
    int p[N],cnt;//质数
    bool isprime[N];
    int s[N];//i中包含的质因子
    int c;//第一个平方大于N的质数的下标
    bool nofree[N];
    void init() {
        mem(isprime,1);
        rep(i,2,N) if(isprime[i]) {
            p[cnt++]=i;
            if(i*i<N)c=cnt;
            r(j,i,N,i) isprime[j]=false;
        }
        rep(i,2,N) rep(j,0,cnt) if(i%p[j]==0) {
            if(j<c && (i/p[j])%p[j]) s[i]|=(1<<j);
            else {
                nofree[i]=true;
                break;
            }
        }
    }
    int main() {
        init();
        scanf("%d",&t);
        while(t--) {
            scanf("%d%d",&n,&m);
            mem(dp,0);
            mem(f,0);
            dp[0][0][0]=f[0][0][0]=1;
    
            rep(i,1,n+1)if(!nofree[i]) {
                rep(j,0,m+1) rep(k,0,1<<c) {
                    add(dp[1][j][k],dp[0][j][k]);//不选i
                    if(j<m && !(k&s[i]))
                        add(dp[1][j+1][k|s[i]],dp[0][j][k]);//选i
                }
                rep(j,0,m+1) rep(k,0,1<<c) dp[0][j][k]=dp[1][j][k],dp[1][j][k]=0;
            }
            rep(i,c,cnt) { //前i个平方大于N的质数
                rep(j,0,m+1) rep(k,0,(1<<c)) { //取j个,含有因子的状态为k
                    add(f[1][j][k],f[0][j][k]);//不选p[i]的倍数
                    if(j<m && f[0][j][k])
                        for(int l=1; p[i]*l<=n; ++l) //取p[i]的l倍
                            if(!nofree[l] && !(k&s[l])) add(f[1][j+1][k|s[l]],f[0][j][k]);
                }
                rep(j,0,m+1) rep(k,0,1<<c) f[0][j][k]=f[1][j][k],f[1][j][k]=0;
            }
            rep(i,0,m) rep(j,0,1<<c) add(f[0][i+1][j],f[0][i][j]);
    
            ll ans=M-1;
            rep(i,0,m+1) rep(j,0,1<<c) if(dp[0][i][j])
                rep(k,0,1<<c) if(!(k&j))
                    add(ans,dp[0][i][j]*f[0][m-i][k]%M);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    1010 Just do it (找规律、递推、Lucas、快速幂)

    题目链接 HDU6129 Just do it

    有一个长度为n的整数序列({a_n}),对其做m次前缀异或和,求最终的序列。(1leq nleq2 imes10^5,1leq mleq10^9,0leq a_ileq2^{30}-1)

    法1.
    i次变换第j个数是(f[i][j]),那么f[i][j]=f[i][j-1]^f[i-1][j](i>0,j>0),a[i]对f[x][y]有贡献当且仅当从f[1][i]走到f[x][y]有奇数条非降路径,也就是C(x-1+y-i,y-i)%2为1。由Lucas定理知C(n,m)%2为1当且经当(n&m)==m。计算出f[m][n],f[m][1..n-1]就是对应错位的a[i]异或起来。当有贡献的a[i]不多时,就不会达到(O(n^2))
    也可以打表找规律。
    法2.
    既然有递推式f[i][j]=f[i][j-1]^f[i-1][j]​,那么就可以:
    f[i][j]=f[i][j-2]^f[i-2][j]​,...,f[i][j]=f[i-2^x][j]^f[i-2^x][j]​。
    然后用快速幂的思想做。

    #include <bits/stdc++.h>
    const int N=200001;
    using namespace std;
    int t,n,m;
    int ans[N],a[N];
    int main(){
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d",&n,&m);
    		memset(ans,0,sizeof ans);
    		for(int i=0;i<n;++i)
    			scanf("%d",a+i);
    		for(int i=0;i<n;++i)
    			if(((m-1+i)&i) == i)//第i+1项中a[1]的系数为C(m-1+i,i)
    				for(int j=i;j<=n;++j)//a[j-i+1]对第j项有贡献
    					ans[j]^=a[j-i+1];
    		for(int i=0;i<n;++i)
    			printf("%d%c",ans[i],i==n-1?'
    ':' ');
    	}
    	return 0;
    }
    
    #include <bits/stdc++.h>
    int t,n,m;
    int a[200001];
    int main(){
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d",&n,&m);
    		for(int i=0;i<n;++i)
    			scanf("%d",a+i);
    		for(int k=1;m;m>>=1,(k<<=1))
    			if(m&1)
    			for(int j=k;j<n;++j)
    				a[j]^=a[j-k];
    		for(int i=0;i<n;++i)
    			printf("%d%c",a[i],i==n-1?'
    ':' ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    提高情商的八种方法
    线程安全与可重入
    【Linux必知必会】initrd.img、vmlinux和 vmlinuz************
    shell调试技术
    (转)DeviceIOControl详解
    软件质量特性及其子特性列表
    【Linux必知必会】initrd.img、vmlinux和 vmlinuz
    驱动程序与应用程序之间共享内存
    调试器GDB
    知道IP地址和子网掩码。算出网络地址、广播地址、地址范围、可用的主机数
  • 原文地址:https://www.cnblogs.com/flipped/p/7367989.html
Copyright © 2011-2022 走看看