zoukankan      html  css  js  c++  java
  • ICPC2021上海

    热身赛

    A

    分类讨论+贪心

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=100005;
    int n,cnt;
    double y[N],d[N],ans,tot_d,ans_d,di;
    double dis(int i,int j){
    	return sqrt(1.0*(j-i)*(j-i)+(y[j]-y[i])*(y[j]-y[i]));
    }
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;++i)
    	cin>>y[i];
    	y[0]=0;y[n+1]=0;
    	for(int i=1;i<=n+1;++i){
    		tot_d+=dis(i-1,i);
    	}
    	//move i,i+1
    	for(int i=1;i<n;++i){
    		di=(dis(i-1,i+2))-(dis(i-1,i)+dis(i,i+1)+dis(i+1,i+2));
    		if(di<ans_d) ans_d=di;
    	}
    	//move i,j |i-j|>1
    	for(int i=1;i<=n;++i){
    		d[i]=dis(i-1,i+1)-(dis(i-1,i)+dis(i,i+1));
    	}
    	double d_min=d[1];
    	for(int i=3;i<=n;++i){
    		d_min=min(d_min,d[i-2]);
    		di=d_min+d[i];
    		if(di<ans_d) ans_d=di;
    	}
    	ans=tot_d+ans_d;
    	printf("%lf",ans);
    	return 0;
    }
    

    B

    题目大意

    对于一个矩阵,初始全为0,每个时刻每个格子 \(a_{i,j}\) 会增加 \(A_i\times B_j\)
    \(q\) 次操作,每次在时刻 \(t_i\) 擦除一行或一列。
    求擦除的数字的总和。

    Solution

    对于每个格子,其对答案的贡献为:最后擦除时刻\(t_{i,j}\times A_i\times B_j\)
    \(ans=\sum A_i\times B_j\times t_{i,j}\\=\sum_{t}t\times(\sum_{t_{Ai}\;\leq\;t}A_i*\sum_{t_{Bj}\;=\;t}B_j+\sum_{t_{Ai}\;=\;t}A_i*\sum_{t_{Bj}\;\leq\;t}B_j)\)
    将矩阵重组,使得行列各按最后擦除时刻升序排列,分别维护当前时刻 \(t\) 可以擦除(即不会在后面时刻被擦除)的行、列前缀和(即 \(\sum_{t_{Ai}\;\leq\;t}A_i\;,\;\sum_{t_{Bj}\;\leq\;t}B_j)\))。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=100005,mod=(1e9+7);
    int n,m,q;
    struct matrix{
    	long long x,t;
    }a[N],b[N];
    bool cmp(matrix a,matrix b){
    	return a.t<b.t;
    }
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;++i){
    		cin>>a[i].x;a[i].x%=mod;
    	}
    	for(int j=1;j<=m;++j){
    		cin>>b[j].x;b[j].x%=mod;
    	}
    	cin>>q;
    	int ty,x,t;
    	while(q--){
    		cin>>ty>>x>>t;
    		if(ty==1) a[x].t=t;
    		else b[x].t=t;
    	}
    	sort(a+1,a+1+n,cmp);
    	sort(b+1,b+1+m,cmp);
    	a[n+1].t=b[m].t+1;
    	b[m+1].t=a[n].t+1;
    	
    	long long sa=0,sb=0,ans=0;
    	int i=1,j=1;
    	while(i<=n&&a[i].t==0){
    		sa=(sa+a[i].x)%mod;++i;
    	}
    	while(j<=m&&b[j].t==0){
    		sb=(sb+b[j].x)%mod;++j;
    	}
    	while(i<=n||j<=m){
    	 	long long t=min(a[i].t,b[j].t);
    	 	for(int k=i;k<=n&&a[k].t==t;++k){
    	 		sa=(sa+a[k].x)%mod;
    		}
    	 	
    	 	for(int k=j;k<=m&&b[k].t==t;++k){
    	 		sb=(sb+b[k].x)%mod;
    		}
    	 		
    		while(i<=n&&a[i].t==t){
    			ans=(ans+a[i].x*sb%mod*a[i].t%mod)%mod;
    			++i;
    		}
    		while(j<=m&&b[j].t==t){
    			ans=(ans+b[j].x*sa%mod*b[j].t%mod)%mod;
    			++j;
    		}
    	}
    	cout<<ans;
    	return 0;
    }
    

    正式赛

    E

    题目大意

    给定一个数列,选出尽可能多的数,满足两两之差大于k,求最多能取到的数字个数。

    Solution

    数列排个序,贪心。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N=100005;
    
    ll a[N],ans;
    int n,k;
    
    int main(){
    	cin>>n>>k;
    	for(int i=1;i<=n;++i)
    	    cin>>a[i];
    	sort(a+1,a+1+n);
    	
    	ll lst=a[1];ans=1;
    	for(int i=2;i<=n;++i)
    	    if(a[i]-lst>=k){
    			lst=a[i];++ans;
    		}
    	cout<<ans;
    }
    

    D

    题目大意

    给定\(p,q\), 求满足\(\frac{p}{q}=\frac{a}{b}+\frac{b}{a}\)的a,b。

    Solution

    等价于\(\begin{cases}ab=4kq\\a^2+b^2=4kp\end{cases}\)

    \(\begin{cases}(a+b)^2=4k(p+2q)\\(a-b)^2=4k(p-2q)\end{cases}\),

    亦即\(\begin{cases}a=\sqrt{k(p+2q)}+\sqrt{k(p-2q)}\\b=\sqrt{k(p+2q)}-\sqrt{k(p-2q)}\end{cases}\)

    有整数解当且仅当\((p+2q)(p-2q)\)是完全平方数。

    为使解最小,即k去掉完全平方数的因子,可行a,b去除gcd。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N=10005,M=10005;
    int pri[M],cnt;
    bool b[N];
    bool issqr(ll x){
    	ll y=sqrt(x);
    	return y*y==x;
    }
    void prime(){
    	for(int i=2;i*i<=10000;++i){
    		for(int j=2;i*j<=10000;++j)
    			b[i*j]=true;
    	}
    	for(int i=2;i<=10000;++i)
    		if(!b[i]) pri[++cnt]=i;
    }
    
    int main()
    {
    	int t,p,q;
    	cin>>t;
    	prime();
    	while(t--){
    		cin>>p>>q;
    		int tmp1=p+(q<<1);
    		int tmp2=p-(q<<1);
    		if(!issqr(1ll*tmp1*tmp2)){
    			printf("%d %d\n",0,0);
    		}
    		else{
    			int k=tmp1;
    			for(int i=1;i<=cnt&&pri[i]*pri[i]<=k;++i){
    				if(!(k%(pri[i]*pri[i]))) k/=(pri[i]*pri[i]);
    			}
    			long long d1=sqrt(1ll*k*tmp1),d2=sqrt(1ll*k*tmp2);
    			
    			printf("%lld %lld\n",(d1+d2)/__gcd(d1+d2,d1-d2),(d1-d2)/__gcd(d1+d2,d1-d2));
    		}
    	}
    }
    

    I

    简单dp

    #include<bits/stdc++.h>
    using namespace std;
    
    const long long inf=1e12;
    const int N=105,K=2600;
    long long f[N][(K<<1)+10][N],v[N];
    bool g[N][(K<<1)+10][N];
    int t[N],n,m;
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;++i)
    		cin>>v[i]>>t[i];
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=(K<<1);++j)
    			for(int k=0;k<=m;++k)
    				f[i][j][k]=-inf;
    	f[1][K][0]=0ll;
    	f[1][t[1]+K][0]=v[1];
    	f[1][-t[1]+K][0]=v[1];
    	f[1][t[1]*2+K][1]=v[1];
    	f[1][-t[1]*2+K][1]=v[1];
    	for(int i=2;i<=n;++i)
    		for(int j=0;j<=(K<<1);++j)
    			for(int k=0;k<=m;++k){
    				if(f[i-1][j][k]!=-inf) f[i][j][k]=max(f[i][j][k],f[i-1][j][k]);
    				if(j+t[i]<=(K<<1)&&f[i-1][j+t[i]][k]+v[i]!=-inf) f[i][j][k]=max(f[i][j][k],f[i-1][j+t[i]][k]+v[i]);
    				if(j-t[i]>=0&&f[i-1][j-t[i]][k]+v[i]!=-inf) f[i][j][k]=max(f[i][j][k],f[i-1][j-t[i]][k]+v[i]);
    				if(k&&j+(t[i]<<1)<=(K<<1)&&f[i-1][j+(t[i]<<1)][k-1]+v[i]!=-inf) f[i][j][k]=max(f[i][j][k],f[i-1][j+(t[i]<<1)][k-1]+v[i]);
    				if(k&&j-(t[i]<<1)>=0&&f[i-1][j-(t[i]<<1)][k-1]+v[i]!=-inf) f[i][j][k]=max(f[i][j][k],f[i-1][j-(t[i]<<1)][k-1]+v[i]);
    			}
    	long long ans=0ll;
    	for(int k=0;k<=m;++k)
    		ans=max(ans,f[n][K][k]);
    	cout<<ans;
    	return 0;
    }
    

    G

    dfs贪心,\(2n\) 个点两两配对的方案数为\((n-1)(n-3)···1\)

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=100005;
    const long long mod=998244353;
    struct graph{
    	int nxt,to;
    }e[N<<1];
    int g[N],deg[N],dep[N],cnt,n;
    bool b[N],leaf[N];
    long long f[N],ans=1;
    void adde(int x,int y){
    	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
    }
    void dfs(int u){
    	leaf[u]=true;
    	for(int i=g[u];i;i=e[i].nxt){
    		if(!dep[e[i].to]){
    			leaf[u]=false;
    			dep[e[i].to]=dep[u]+1;
    			dfs(e[i].to);
    			if(b[e[i].to]) --deg[u];
    		}
    	}
    	if(leaf[u]) return;
    	if(deg[u]&1) deg[u]--;
    	else b[u]=true;
    }
    int main(){
    	f[0]=1;
    	for(int i=2;i<N;i+=2)
    		f[i]=1ll*(i-1)*f[i-2]%mod;
        scanf("%d",&n);
        for(int i=1,x,y;i<n;++i){
        	scanf("%d%d",&x,&y);
        	adde(x,y);adde(y,x);
        	++deg[x];++deg[y];
    	}
    	dep[1]=1;
    	dfs(1);
    	for(int i=1;i<=n;++i)
    		if(!leaf[i]){
    			ans=(ans*f[deg[i]])%mod;
    		}
    	cout<<ans;
    }
    

    H

    题目大意

    n个城市,每个城市有活力值,每条边有权值。
    所拥有的活力值大于边权的时候可以通过,通过一条边不消耗活力值。
    每个城市只能打一次工,打工无门槛,可以获得这个城市的活力值。
    q次询问,求给定初始城市和初始活力值能达到的最大活力值。

    Solution

    离线处理:对并查集过程的各状态建个树,边权为通过这条边到达父亲需要的初始活力值(负数和0都相当于没有)。
    对于每个查询,倍增跳到能到达的最高点,以其为根的子树的活力值+初始活力值即为答案。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=100005,K=19;
    struct edge{
    	int u,v,w; 
    }ed[N];
    struct graph{
    	int nxt,to,w;
    }e[N<<1];
    int g[N<<1],s[N<<1],f[N<<1][K],w[N<<1][K];
    int a[N],fa[N],p[N],n,m,q,cnt,p_cnt;
    bool cmp(edge a,edge b){
    	return a.w<b.w;
    }
    int gf(int u){
    	if(fa[u]==u) return u;
    	return fa[u]=gf(fa[u]);
    }
    void adde(int u,int v,int w){
    	e[++cnt].nxt=g[u];g[u]=cnt;e[cnt].to=v;e[cnt].w=w;
    }
    void dfs(int u){
    	for(int i=g[u];i;i=e[i].nxt){
    		dfs(e[i].to);
    		s[u]+=s[e[i].to];
    		e[i].w-=s[e[i].to];
    	}
    	if(u<=n) s[u]+=a[u];
    }
    void dfs1(int u){
    	for(int i=1;i<K;++i){
    		f[u][i]=f[f[u][i-1]][i-1];
    		w[u][i]=max(w[u][i-1],w[f[u][i-1]][i-1]);
    	}
    	for(int i=g[u];i;i=e[i].nxt){
    		f[e[i].to][0]=u;
    		w[e[i].to][0]=e[i].w;
    		dfs1(e[i].to);
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	for(int i=1;i<=m;++i)
    		scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w);
    	
    	for(int i=1;i<=n;++i){
    		fa[i]=p[i]=i;
    	}
    	p_cnt=n;
    	sort(ed+1,ed+1+m,cmp);
    	for(int i=1;i<=m;++i){
    		if(gf(ed[i].u)!=gf(ed[i].v)){
    			++p_cnt;
    			adde(p_cnt,p[fa[ed[i].u]],ed[i].w);
    			adde(p_cnt,p[fa[ed[i].v]],ed[i].w);
    			fa[gf(ed[i].u)]=gf(ed[i].v);
    			p[gf(ed[i].u)]=p_cnt;
    		}
    	}
    	
    	dfs(p_cnt);dfs1(p_cnt);
    	
    	int x,k;
    	while(q--){
    		scanf("%d%d",&x,&k);
    		if(k<w[x][0]){
    			printf("%d\n",k+a[x]);
    			continue;
    		}
    		for(int i=K-1;i>=0;--i){
    			if(k>=w[x][i]&&f[x][i]){
    				x=f[x][i];
    			}
    		}
    		printf("%d\n",k+s[x]);
    	}
    	
    	return 0;
    }
    

    K

    题目大意

    求一个长度为n的01串,每个时刻都不全为0,每个1都会往两边走,相撞或出界就消失,要求2n时刻内出现循环。

    Solution

    构造题。
    \(n=2\)时,\(10\)\(01\)
    \(n=3\)时,无解;
    \(n\)为偶数时,\(10、01\)交替拼接;
    \(n\)为奇数时,\(010+(10+01)_k\)

    #include <bits/stdc++.h>
    using namespace std;
    
    int n;
    int main(){
    	cin>>n;
    	if(n&1){
    		if(n==3){
    			cout<<"Unlucky";
    			return 0;
    		}
    		cout<<"010";
    		for(int i=1;i<=((n-3)>>1);++i)
    			if(i&1) cout<<"10";
    			else cout<<"01"; 
    	}
    	else{
    		for(int i=1;i<=(n>>1);++i)
    			if(i&1) cout<<"01";
    			else cout<<"10"; 
    	}
    	return 0;
    }
    
  • 相关阅读:
    Python 从零学起(纯基础) 笔记 之 collection系列
    ARM学习 之 如何在向内核写入系统调用
    idea的git使用案例
    idea使用git的pull命令报错1
    String、StringBuilder以及StringBuffer
    HashMap实现原理及源码分析
    logback使用注意点1
    创建zookeeper集群
    disconf安装问题
    linux更换jdk版本
  • 原文地址:https://www.cnblogs.com/AireenYe/p/15765191.html
Copyright © 2011-2022 走看看