zoukankan      html  css  js  c++  java
  • 9.11 test

    题面.pdf

    T1:
    通过打表发现,从一个点出发只有距离为1,2,3,5,6,9,10,13,17的点才不能到达;

    那么我们转移的时候只有距离在20以内才不一定能直接转移,那么我们离散化之后;

    对于每一个矿往前for 20个,然后特判距离是否合法进行转移即可;

    //MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=200050;
    int tot,hsh[N],a[N],v[N],b[N],n,m;
    int dp[N];
    bool check(int x){
        if(x==1||x==2||x==3||x==5||x==6||x==9||x==10||x==13||x==17) return 0;
        return 1;
    }
    int main(){
        freopen("mining.in","r",stdin);
        freopen("mining.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
    	scanf("%d%d",&a[i],&b[i]);
    	hsh[++tot]=b[i];
        }
        sort(hsh+1,hsh+1+tot);tot=unique(hsh+1,hsh+1+tot)-hsh-1;
        for(int i=1;i<=n;i++){
    	b[i]=lower_bound(hsh+1,hsh+1+tot,b[i])-hsh;
    	v[b[i]]+=a[i];
        }
        for(int i=1;i<=tot;i++) dp[i]=-(1e9+1);
        for(int i=1;i<=tot;i++){
    	    for(int j=max(0,i-20);j<i;j++){
    		if(check(hsh[i]-hsh[j])) dp[i]=max(dp[i],dp[j]+v[i]);
    	    }
        }
        int ans=0;
        for(int i=1;i<=tot;i++) /*cout<<hsh[i]<<' '<<dp[i]<<endl,*/ans=max(ans,dp[i]);
        printf("%d
    ",ans);
        return 0;
    }
    

    T2:

    首先因为方差只会越变越大,所以可以加剪枝进行搜索,但是双向搜索还是无法100%;

    考虑到把答案的式子拆开变为(n+m-1)*∑ai^2-sum*sum,我们发现答案只和sum和ai^2这两个量有关;

    那么我们可以用一种很套路的动态规划,在状态中开一维表示其中一个量,然后用dp的转移过程中,求出该状态下另一个量的最优;

    那么我们设dp[i][j][k],表示到i行j列,sum为k的,∑ai^2的最小值,然后转移就是很simple了,最后枚举第三维的状态,然后算出该状态下的最优值;

    //MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=2000;
    int a[40][40],dp[40][40][N],T,n,m;
    int main(){
        freopen("matrix.in","r",stdin);
        freopen("matrix.out","w",stdout);
        scanf("%d",&T);
        while(T--){
    	int n,m;scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    	    for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
    	}
    	memset(dp,127/3,sizeof(dp));int sum=30*(n+m-1);
    	dp[1][1][a[1][1]]=a[1][1]*a[1][1];
    	for(int i=1;i<=n;i++){
    	    for(int j=1;j<=m;j++){
    		for(int k=0;k<=sum;k++){
    		    if(dp[i][j][k]==dp[0][0][0]) continue;
    		    if(i+1<=n) dp[i+1][j][k+a[i+1][j]]=min(dp[i+1][j][k+a[i+1][j]],dp[i][j][k]+a[i+1][j]*a[i+1][j]);
    		    if(j+1<=m) dp[i][j+1][k+a[i][j+1]]=min(dp[i][j+1][k+a[i][j+1]],dp[i][j][k]+a[i][j+1]*a[i][j+1]);
    		}
    	    }
    	}
    	int ans=dp[0][0][0];
    	for(int i=1;i<=sum;i++){
    	    if(dp[n][m][i]==dp[0][0][0]) continue;
    	    ans=min(ans,(n+m-1)*dp[n][m][i]-i*i);
    	}
    	printf("%d
    ",ans);
        }
    }
    

     T3:

    首先M=0的做法,就是以父亲来转移,ans[x]=ans[fa[x]-size[x]*w+(n-size[x])*w;

    然我们考虑M不为0的情况,因为M很小,二进制中只有4位,那么我们把一个二进制数分成两边来计算;

    后4位中的是要受xor影响的,但是前面都是不用管xor的影响的;

    那么我们对于一个x开两个数组,sum[x]表示除开后4位的数值,(最后算答案的时候<<4),f[x][i](i=0~15),表示跟x距离为i的点的数量;

    那么我们考虑如何合并,其实相当于模拟二进制进位运算;

    设父亲为 x+y,儿子为a+b,那么合并后相当于变为,((x+y)+(a+b)/16)+(a+b)%16;

    也就是在加法的过程中处理进位,在具体处理过程中,我们考虑边的贡献来进行合并,因为相当于是儿子中所有距离要+w;

    那么我们先考虑这个东西对前面的贡献,即(w/16)*size[son];

    然后在考虑这个东西堆后面四位的贡献,即枚举0-15,+w,然后处理进位;

    这样第一遍dfs就解决了子树内的问题,然后我们考虑父亲对儿子的贡献;

    这里有一个常用技巧,就是用一个数组存下父亲除去该儿子的子树的答案,然后就可以“颠倒”父子关系,把父亲当儿子进行处理,处理方法就和第一遍dfs一样;

    最后处理答案的时候就是前面的答案<<4,然后再把后面的0-15 xor m再乘以数量加入答案;

    //MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=200050;
    int dp[N][16],c[16],sum[N];
    int head[N],to[N],nxt[N],v[N],cnt,dis[N],size[N],ans[N],n,m;
    void lnk(int x,int y,int z){
        to[++cnt]=y,nxt[cnt]=head[x],v[cnt]=z,head[x]=cnt;
        to[++cnt]=x,nxt[cnt]=head[y],v[cnt]=z,head[y]=cnt;
    }
    void dfs1(int x,int f){
        dp[x][0]=1;size[x]=1;
        for(int i=head[x];i;i=nxt[i]){
    	int y=to[i];if(y==f) continue;
    	dfs1(y,x);int w=v[i];
    	sum[x]+=sum[y]+(w/16)*size[y];
    	int g=w%16;
    	for(int j=0;j<=15;j++){
    	    dp[x][(j+g)%16]+=dp[y][j];
    	    sum[x]+=dp[y][j]*((j+g)/16);
    	}
    	size[x]+=size[y];
        }
    }
    void dfs2(int x,int f){
        ans[x]=sum[x]<<4;
        for(int i=0;i<=15;i++) ans[x]+=(i^m)*dp[x][i];
        for(int i=head[x];i;i=nxt[i]){
    	int y=to[i];if(y==f) continue;
    	int w=v[i],ret=sum[x]-sum[y]-(w/16)*size[y],g=w%16;
    	memset(c,0,sizeof(c));
    	for(int j=0;j<=15;j++){
    	    ret-=dp[y][j]*((j+g)/16);
    	    c[(j+g)%16]=dp[x][(j+g)%16]-dp[y][j];
    	}
    	ret+=(w/16)*(n-size[y]);
    	for(int j=0;j<=15;j++){
    	    dp[y][(j+g)%16]+=c[j];
    	    sum[y]+=c[j]*((j+g)/16);
    	}
            sum[y]+=ret;dfs2(y,x);
        }
    }
    int main(){
        freopen("warehouse.in","r",stdin);
        freopen("warehouse.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
    	int x,y,z;scanf("%d%d%d",&x,&y,&z);lnk(x,y,z);
        }
        dfs1(1,0);dfs2(1,0);
        for(int i=1;i<=n;i++) printf("%d
    ",ans[i]-m);
        return 0;
    }
    
  • 相关阅读:
    高级语言是面向用户的
    汇编语言指令是机器指令的符号化
    程序设计语言具有心理工程及技术
    语言的种类成分
    程序设计方法和过程
    程序设计的基本概念
    结构化程序设计与非结构化程序设计之分
    常见语言计算机语言
    语言的基础是一组记号和一组规则
    面向对象的基本概念
  • 原文地址:https://www.cnblogs.com/qt666/p/7506725.html
Copyright © 2011-2022 走看看