zoukankan      html  css  js  c++  java
  • Vjudge contest 425061

    Problem A

    乱搞一下就可以了。

    代码

    #pragma GCC optimize(2)
    
    #include<bits/stdc++.h>
    using namespace std;
    const int N=55;
    int n,a[N],b[N],cnt[N],res=0;
    void dfs(int now){
    	if(now>n){
    		int tmp=1e9+7,lst=-1e9-7;
    		for(int i=0;i<24;++i){
    			if(cnt[i]) tmp=min(tmp,i-lst),lst=i;
    			if(cnt[i]>1){tmp=0;break;}
    		}
    		// for(int i=0;i<24;++i) printf("%d ",cnt[i]);
    		// printf("
    ");
    		tmp=min(tmp,24-lst),res=max(res,tmp);
    		return ;
    	}
    	b[now]=a[now],cnt[b[now]]++;
    	dfs(now+1),cnt[b[now]]--;
    	if(a[now]&&!cnt[24-a[now]]){
    		b[now]=24-a[now],cnt[b[now]]++;
    		dfs(now+1),cnt[b[now]]--;
    	}
    }
    int main(){
    	cin>>n,cnt[0]++;
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	dfs(1);return printf("%d
    ",res),0;
    }
    

    Problem B

    你暴搜一下找一波规律就可以了,然后发现在 (k-1) 为质数的时候才能满足一种更简单的构造方法,然后就可以了。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e3+5,K=2e3+5;
    int n,k,a[N][K];
    int used[N][N],cnt[N];
    void dfs(int x,int y){
    	if(y>k){
    		for(int i=1;i<x;++i){
    			int cnt=0;
    			for(int c=1;c<=k;++c){
    				for(int d=1;d<=k;++d)
    				cnt+=(a[i][c]==a[x][d]);
    			}
    			if(cnt!=1) return ;
    		}
    		return dfs(x+1,1);
    	}
    	if(x>n){
    		for(int i=1;i<=n;++i){
    			for(int j=1;j<=k;++j)
    			printf("%d ",a[i][j]);
    			printf("
    ");
    		}
    		exit(0);
    	}
    	for(int i=1;i<=n;++i){
    		if(used[x][i]||cnt[i]>=k) continue;
    		used[x][i]=true,cnt[i]++;
    		a[x][y]=i,dfs(x,y+1);
    		used[x][i]=false,cnt[i]--;
    	}
    }
    int main(){
    	// scanf("%d%d",&n,&k);
    	// dfs(1,1);
    	n=1407,k=38;
    	printf("%d %d
    ",n,k);
    	for(int i=1;i<=k;++i) printf("%d ",i);
    	printf("
    ");
    	for(int i=1;i<k;++i){
    		for(int j=1;j<k;++j)
    		a[i][j]=i*(k-1)+j+1;
    	}
    	for(int i=1;i<k;++i){
    		printf("1 ");
    		for(int j=1;j<k;++j)
    		printf("%d ",a[i][j]);
    		printf("
    ");
    	}
    	for(int i=1;i<k;++i){
    		printf("2 ");
    		for(int j=1;j<k;++j)
    		printf("%d ",a[j][i]);
    		printf("
    ");
    	}
    	for(int i=3;i<=k;++i){
    		for(int j=1;j<k;++j){
    			printf("%d ",i);
    			for(int c=1,d=j;c<k;++c,d=(d+i-3)%(k-1)+1){
    				printf("%d ",a[c][d]);
    			}
    			printf("
    ");
    		}
    	}
    	return 0;
    }
    

    Problem C

    就是考虑对于若干个点,如果你考虑让这些点联通,必定可以通过其中的边来使得这几个点的权值相同。考虑对于一个点集,如果所有点的权值和是 (A) ,联通这些点的最小的边权和 (B) ,点数为 (k) ,那么可以证明(证明略),那么每一个都可以到达的点值为 (frac{max(A-B,0)}{k})

    这样的话对于每一个子集,我们定义 (f_i) 表示子集 (i) 所有点至少可以变成多少,这样状态转移方程就是:

    [f_i=max_{jin i}min(f_j,f_{complement_ij}) ]

    然后这个枚举子集和每一个子集的子集复杂度的证明在这里

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=16;
    int n;bool used[N];
    struct City{double x,y,w;}a[N];
    struct Edge{int u,v;double w;};
    bool cmp(Edge a,Edge b){return a.w<b.w;}
    struct DSU{
    	int fa[N];
    	void init(int n){for(int i=1;i<=n;++i) fa[i]=i;}
    	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    	bool merge(int u,int v){
    		int fu=find(u),fv=find(v);
    		if(fu!=fv) return fa[fu]=fv,true;
    		return false;
    	}
    }d;
    vector<Edge> bag;double f[1<<N];
    double dis(City a,City b){
    	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    double cal(int x){
    	double res=0,cnt=0;
    	bag.clear(),d.init(n);
    	memset(used,0,sizeof(used));
    	for(int i=1;i<=n;++i) used[i]=(x&(1<<(i-1)));
    	for(int i=1;i<=n;++i) res+=used[i]*a[i].w,cnt+=used[i];
    	for(int i=1;i<=n;++i){
    		if(!used[i]) continue;
    		for(int j=i+1;j<=n;++j){
    			if(!used[j]) continue;
    			bag.push_back((Edge){i,j,dis(a[i],a[j])});
    		}
    	}
    	sort(bag.begin(),bag.end(),cmp);
    	for(int i=0;i<(int)bag.size();++i)
    	res-=d.merge(bag[i].u,bag[i].v)*bag[i].w;
    	return res=max(res,0.0),res/cnt;
    }
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;++i){
    		scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].w);
    	}
    	for(int i=1;i<(1<<n);++i){
    		f[i]=cal(i);
    		for(int j=i;j;j=(j-1)&i)
    		f[i]=max(f[i],min(f[j],f[i^j]));
    	}
    	return printf("%.10lf
    ",f[(1<<n)-1]),0;
    }
    

    Problem D

    问了一圈看了一堆题解还是没有懂,决定自己思考做出来。(我太弱了)


    首先需要明确的是,我们要求的是不同的染色方案数!!!

    然后我们不妨来考虑一下一个只有向右和向下的特殊情况。

    然后你会发现他一定满足有一个向右下延伸的颜色的分界线,为什么呢?

    我们首先比较显然的,若图上有 (n) 个向右的, (m) 个向下的,我们可以将其简化为一个 (n imes m) 的矩形。

    然后你首先对于 ((1,1)) 这个格子,你发现对于他的颜色,实际上是决定了是第一行的机器人先走还是第一列的机器人先走,如果是第一行的先走,你就可以往右一格,继续考虑是第一行的先走还是第二列的先走……如此类推,你发现最终的一种涂色状态必然对应一种由 ((1,1)) 走到 ((n+1,m+1)) 的方案。

    为什么是 ((n+1,m+1)) 呢?实际上我们颜色的比较到达第 (n+1) 行或者第 (m+1) 列是就可以结束了,相当于是要统计从 ((1,1)) 到第 (n+1) 行和第 (m+1) 列的总方案数,此数值正好等于到达 ((n+1,m+1)) 的方案数。

    以上,我们相当于证明了一个只有 (n) 个向右的和 (m) 个向下的位置的方案数为 (inom{n+m}{n})

    我们需要将这个东西扩展一下。我们考虑再加一个向上的方向该如何计算呢?

    沿用我们上面的思路,相当于一个位置我们可能要考虑三个方向的先后,好像不好办了。

    但是如果你发现如果存在一条横线将其横向划分开,那么他就被转化为了我们上述的子问题了。

    我们不妨设向右的有 (n) 个,向下的有 (m) 个,向上的有 (k) 个,我们钦定选择一个横向的连上,然后同时强制上半部分不能同时出现横向连满的,下半部分却可以,那么式子是:

    [f(n,m,k)=sum_{i=1}^ninom{i+m-2}{m-1}inom{n-i+k}{k}\ =inom{n+m+k-1}{n-1} ]

    目前为止,我们已经解决了有三个方向的问题,式子如上。

    其实你可以将两个式子分别看成你要走的两个方向的方案数,然后这个 (sum) 实际上可以看成是枚举你在什么位置向右(或向上,总之就是不是和 (i) 在同一个维度上的方向)走一步,然后整个式子就变成了一个组合数。

    然后考虑四个方向的怎么办,有一种方法就是枚举如何将整个矩形分解成三个方向的,复杂度应该是 (O(n^2)) 的,然后你考虑前缀和优化一下就可以了吧。

    我来试一试。


    然后你发现,如果这么做的话有很多的样例是过不了的,一下有几个需要特判的地方。

    1. 如果没有机器人,输出 (1)
    2. 考虑横竖两个方向都可以将整个矩形分成两个只有三个方向的矩形。
    3. 在考虑三个方向的问题时,如果 (n)(0) ,就不能用公式计算了,具体如何计算可以自己思考。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N=1e5+5;
    const int MOD=998244353;
    int n,m,fac[N<<2],ifac[N<<2];
    int a[N],b[N],c[N],d[N];//r,l,d,u
    int f[N],res=0;
    int ksm(int x,int k){
    	int res=1;
    	for(;k;k>>=1,x=x*x%MOD)
    	if(k&1) res=res*x%MOD;
    	return res;
    }
    int cal(int n,int m){
    	if(n<0||m<0||n<m) return 1;
    	return fac[n]*ifac[m]%MOD*ifac[n-m]%MOD;
    }
    void solve(){
    	memset(f,0,sizeof(f));
    	int cnt1=0,cnt2=0,cnt3=0;
    	for(int i=1;i<=n;++i) cnt1+=a[i];
    	for(int i=1;i<=n;++i) cnt2+=b[i];
    	if(!cnt1&&!cnt2){
    		for(int i=1;i<=m;++i) cnt3+=(c[i]&&d[i]);
    		res+=ksm(2,cnt3),res%=MOD;
    		return ;
    	}
    	if(!cnt2){
    		swap(cnt1,cnt2);
    		for(int i=1;i<=n;++i) swap(a[i],b[i]);
    		for(int i=1;i<=(m>>1);++i) swap(c[i],c[m-i+1]);
    		for(int i=1;i<=(m>>1);++i) swap(d[i],d[m-i+1]);
    	}
    	if(cnt1){
    		for(int i=1;i<=m;++i){
    			if(c[i]||d[i]) f[i]=cal(cnt1+cnt3-1,cnt1-1);
    			cnt3+=c[i]+d[i];
    		}
    		for(int i=1;i<=m;++i){
    			f[i]+=f[i-1],f[i]%=MOD;
    			if(c[i]&&d[i]) f[i]<<=1,f[i]%=MOD;
    		}
    	}
    	else{
    		for(int i=1;i<=m;++i){
    			cnt3+=(c[i]&&d[i]);
    			if(c[i]||d[i]) f[i]=ksm(2,cnt3);
    		}
    	}
    	cnt3=0;
    	for(int i=m;i>=1;--i){
    		if(c[i]||d[i]) res+=f[i]*cal(cnt2+cnt3-1,cnt2-1)%MOD,res%=MOD;
    		cnt3+=c[i]+d[i];
    	}
    }
    signed main(){
    	cin>>n>>m,fac[0]=ifac[0]=1;
    	for(int i=1;i<=n+n+m+m;++i) fac[i]=fac[i-1]*i%MOD;
    	for(int i=1;i<=n+n+m+m;++i) ifac[i]=ksm(fac[i],MOD-2);
    	int cnt=0;
    	for(int i=1;i<=n;++i) scanf("%1lld",&a[i]),cnt+=a[i];
    	for(int i=1;i<=n;++i) scanf("%1lld",&b[i]),cnt+=b[i];
    	for(int i=1;i<=m;++i) scanf("%1lld",&c[i]),cnt+=c[i];
    	for(int i=1;i<=m;++i) scanf("%1lld",&d[i]),cnt+=d[i];
    	if(!cnt) return printf("1"),0;
    	solve(),swap(n,m),swap(a,d),swap(a,b),swap(a,c),solve();
    	return printf("%lld
    ",res),0;
    }
    
  • 相关阅读:
    C和C++的不同点
    音频质量评价指标
    常用函数整理
    Subband Decomposition
    Stability Analysis of Algorithms
    Time Frequency (T-F) Masking Technique
    雅克比(Jacobi)方法
    寒假3
    寒假作业二
    寒假 2
  • 原文地址:https://www.cnblogs.com/Point-King/p/14488191.html
Copyright © 2011-2022 走看看