zoukankan      html  css  js  c++  java
  • 2021.06.21模拟赛

    题面
    写在前面:总算正常发挥了一次没有出降智错误,rank 4,好耶!

    A.分割

    DP+前缀和+排序+离散化+树状数组优化
    (f_i) 是以i结尾的方案数,要注意 (sum_i=0) 的时候 (f_i) 也有一种方案。
    (code)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define maxn 100010
    #define int long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    const int mod=1000000009;
    int n,x;
    int b[maxn],t[maxn],f[maxn];
    struct data{
    	int sum;
    	int id;
    }a[maxn];
    
    bool cmp(data x,data y){
    	if(x.sum!=y.sum) return x.sum<y.sum;
    	return x.id<y.id;
    }
    
    void add(int x,int k){
    	for(;x<=n;x+=x&-x) (b[x]+=k)%=mod;
    }
    
    int ask(int x){
    	int res=0;
    	for(;x;x-=x&-x) (res+=b[x])%=mod;
    	return res;
    }
    
    signed main(){
    	read(n);
    	for(int i=1;i<=n;i++){
    		read(x);
    		a[i].sum=a[i-1].sum+x;
    		a[i].id=i;
    		if(a[i].sum>=0) f[i]=1;
    	}
    	sort(a+1,a+n+1,cmp);
    	for(int i=1;i<=n;i++) t[a[i].id]=i;
    	for(int i=1;i<=n;i++){
    		(f[i]+=ask(t[i]))%=mod;
    		add(t[i],f[i]);
    	}
    	printf("%lld
    ",f[n]);
    	return 0;
    }
    /*
    4
    2
    3
    -3
    1
    //
    4
    */
    

    B.子序列

    一开始求成子序列的数值之和了没注意到是权值和
    最后写了30pts的暴搜,过了样例1,样例2过不去,一边写别的题一边让它接着跑样例2,到了比赛结束跑了两个半小时都没跑完...中午吃饭忘了关程序,回来发现还在跑...跑了五个小时愣是没跑完 (n=1000) 的样例2...事实证明指数级的dfs果然慢的一批这不废话吗
    先放一个能跑一年的暴搜:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define maxn 100010
    #define int long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    const int mod=10007;
    int n,a[maxn];
    int cnt,k,mp[maxn],tong[maxn],ok[maxn],sum1,sum2;
    
    int qpow(int x,int y){
    	int res=1;
    	for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;
    	return res%mod;
    }
    
    void dfs(int *b,int siz,int *ok,int dep){
    	if(siz==dep){
    		int cnt=0;
    		bool flag=0;
    		memset(mp,0,sizeof(mp));k=0;
    		for(int i=0;i<siz;i++){
    //			cout<<b[i]<<"***"<<ok[i]<<" ";
    			if(ok[i]){
    				mp[++k]=i;
    				if(!tong[b[i]]) tong[b[i]]=1,cnt++;
    			}
    		}
    //		cout<<endl;
    		
    		sum1=(sum1+cnt)%mod;
    //		cout<<sum1<<"*"<<endl;
    //		cout<<mp[1]<<" ";
    		for(int i=2;i<=k;i++){
    			if(mp[i-1]+1!=mp[i]) flag=1;
    //			cout<<mp[i]<<" ";
    		}
    //		cout<<endl;
    		if(!flag) sum2=(sum2+cnt)%mod;
    //		cout<<sum2<<"**"<<endl;
    		for(int i=0;i<siz;i++){
    			if(tong[b[i]]) tong[b[i]]=0;
    		}
    //		for(int i=0;i<siz;i++){
    //			if(ok[i]) printf("%lld ",b[i]);
    //		}
    //		printf("
    ");
    //		cout<<"********"<<endl;
    		return ;
    	}
    	else{
    		ok[dep]=0;
    		dfs(b,siz,ok,dep+1);
    		ok[dep]=1;
    		dfs(b,siz,ok,dep+1);
    	}
    }
    
    signed main(){
    //	freopen("ex_seq2.in","r",stdin);
    	read(n);
    	for(int i=0;i<n;i++) read(a[i]);
    	dfs(a,n,ok,0);
    	cout<<sum1<<" "<<sum2<<endl;
    	return 0;
    }
    /*
    3
    2 3 2
    //
    10 9
    */
    

    上正解
    (O(n)) 枚举位置,把它变成计数类问题
    第一个问题:求所有子序列的权值和
    考虑当前这位数对答案的贡献
    所以这个数必须在这个序列中第一次出现,若它前面已经有一样的数了那必然会去重,而当前这个数就是被去掉的那个,对答案就没有贡献了
    我们开个桶 (c) 来记录第 (i) 位前面有多少个值和它相同的数,它贡献的答案就是 (2^{n-c_i})
    第二个问题:求所有连续子序列的权值和
    (l_i)(i) 前面与它最后一个相同的数的位置,贡献的答案为 ((n-i+1)*(i-l_i))
    因为 (a_i) 可能很大所以需要离散化
    (code)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define maxn 100010
    #define int long long
    #define pii pair<long long,long long>
    #define fir first
    #define sec second
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    const int mod=10007;
    int n;
    pii a[maxn];
    int c[maxn],num[maxn],l[maxn],p[maxn],sum1,sum2,cnt;
    
    bool cmp(pii x,pii y){
    	return x.fir<y.fir;
    }
    
    signed main(){
    	read(n);
    	for(int i=1;i<=n;i++){
    		read(a[i].fir);
    		a[i].sec=i;
    	}
    	sort(a+1,a+n+1,cmp);
    	for(int i=1;i<=n;i++){
    		if(a[i-1].fir!=a[i].fir) cnt++;
    		num[a[i].sec]=cnt;
    	}
    	p[0]=1;
    	for(int i=1;i<=n;i++) p[i]=(2*p[i-1])%mod;
    	for(int i=1;i<=n;i++){
    		c[num[i]]++;
    		(sum1+=p[n-c[num[i]]])%=mod;
    		(sum2+=(n-i+1)*(i-l[num[i]])%mod)%=mod;
    		l[num[i]]=i;
    	}
    	cout<<sum1<<" "<<sum2<<endl;
    	return 0;
    }
    /*
    3
    2 3 2
    //
    10 9
    */
    

    C.昊昊的故事1之昊昊的崛起

    果然只会敲暴力
    定义 (d(x,y))(x,y) 两点间路径上最小边权
    因为求的是

    [max { sum d(x,i) } (1 le i le n) ]

    所以很容易想到求LCA,我们可以用树上倍增处理
    但是写的极长还一度以为自己写假了差点自闭
    60pts代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<bits/stdc++.h>
    #define maxn 500010
    //#define int long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    const int inf=0x3f3f3f3f;
    int n;
    int f[maxn][25],dp[maxn][25],p[maxn],dep[maxn],vis[maxn],sum,tmp,ans;
    int cnt=1,head[maxn];
    struct node{
    	int to;
    	int w;
    };
    vector<node> g[maxn]; 
    struct edge{
    	int x;
    	int y;
    	int w;
    }e[2*maxn];
    
    bool cmp(edge a,edge b){
    	return a.w>b.w;
    }
    
    int find(int x){
    	return x==p[x]?x:find(p[x]);
    }
    
    void init(){
    	for(int i=0;i<=n;i++) p[i]=i;
    	for(int i=0;i<=n;i++){
    		for(int j=0;j<=n;j++){
    			dp[i][j]=inf;
    		}
    	}
    }
    
    void pre(){
    	int tot=0;
    	for(int i=1;i<=n-1;i++){
    		int x=e[i].x,y=e[i].y,w=e[i].w;
    		int dx=find(x),dy=find(y);
    		if(dx!=dy){
    			p[dx]=dy;
    			node b;
    			b.to=y;
    			b.w=w;
    			g[x].push_back(b);
    			b.to=x;
    			g[y].push_back(b);
    			tot++;
    			if(tot>=n-1) break;
    		}
    	}
    }
    
    void dfs(int u,int fa,int step,int w){
    	vis[u]=1;
    	if(fa) f[u][0]=fa,dp[u][0]=w;
    	dep[u]=step;
    	int len=g[u].size();
    	for(int i=0;i<len;i++){
    		int to=g[u][i].to;
    		if(to==fa)continue;
    		dfs(to,u,step+1,g[u][i].w);
    	}
    }
    
    void work(){
    	for(int j=1;j<=19;j++){
    		for(int i=1;i<=n;i++){
    			f[i][j]=f[f[i][j-1]][j-1];
    		}
    	}
    	for(int j=1;j<=19;j++){
    		for(int i=1;i<=n;i++){
    			dp[i][j]=min(dp[i][j-1],dp[f[i][j-1]][j-1]);
    		}
    	}
    }
    int lca(int x,int y){
    	int res=inf;
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=19;i>=0;i--){
    		if(dep[f[x][i]]>=dep[y]){
    			res=min(res,dp[x][i]);
    			x=f[x][i];
    		}
    	}
    	if(x==y) return res;
    	for(int i=19;i>=0;i--){
    		if(f[x][i]!=f[y][i]){
    			res=min(res,min(dp[x][i],dp[y][i]));
    			x=f[x][i],y=f[y][i];
    		}
    	}
    	res=min(res,min(dp[x][0],dp[y][0]));
    	return res;
    }
    
    int main(){
    	read(n);
    	for(int i=1;i<=n-1;i++)read(e[i].x),read(e[i].y),read(e[i].w);
    	sort(e+1,e+n-1+1,cmp);
    	init();
    	pre();
    	for(int i=1;i<=n;i++){
    		if(!vis[i]) dfs(i,0,1,inf);
    	}
    	work();
    	for(int i=1;i<=n;i++){
    		sum=0;
    		for(int j=1;j<=n;j++){
    			if(i!=j){
    				sum+=lca(i,j);
    			}
    		}
    		ans=max(ans,sum);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    /*
    4
    1 2 2
    2 4 1
    2 3 1
    //
    4
    */
    

    正解是并查集
    将边权从大到小排序,设当前边为 ((x,y,w)) ,假设有两个连通块,左边的最优值是 (A) ,右边的最优值是 (B)(x subset A)(y subset B)。通过当前的边使左右联通, 最大值就是 (max(A+siz_y*w,B+siz_x*w))
    由此得出用并查集维护,每次合并时执行求最大值的操作,这样合并到最后就是答案
    (code)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<bits/stdc++.h>
    #define maxn 500010
    //#define int long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    int n;
    int fa[maxn],d[maxn];
    struct node{
    	int x;
    	int y;
    	int w;
    }e[2*maxn];
    //struct ST{
    //	int x;
    //	int y;
    //	int w;
    //	int sizx;
    //	int sizy;
    //}st[maxn];
    
    bool cmp(node a,node b){
    	return a.w>b.w;
    }
    
    struct bcj{
    	int fa[maxn],siz[maxn];
    	void init(int n){
    		for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    	}
    	int find(int x){
    		return (x==fa[x])?x:fa[x]=find(fa[x]);
    	}
    	void u(int x,int y,int w){
    		x=find(x),y=find(y);
    		if(x==y) return ;
    		if(siz[x]>siz[y]) swap(x,y);
    		d[y]=max(d[x]+siz[y]*w,d[y]+siz[x]*w);
    		siz[y]+=siz[x];
    		fa[x]=y;
    	}
    }s;
    
    int main(){
    	read(n);
    	for(int i=1;i<=n-1;i++) read(e[i].x),read(e[i].y),read(e[i].w);
    	s.init(n);
    	sort(e+1,e+n-1+1,cmp);
    	for(int i=1;i<=n-1;i++){
    		s.u(e[i].x,e[i].y,e[i].w);
    	}
    	cout<<d[s.find(1)]<<endl;
    	return 0;
    }
    /*
    4
    1 2 2
    2 4 1
    2 3 1
    //
    4
    */
    

    D.机房的人民摸鱼家

    太ex了细节太多了
    不会,没写,先咕了

  • 相关阅读:
    JavaIO学习笔记(五)
    Java学习笔记(四)
    Java学习笔记(三)
    Java学习笔记(二)
    自己动手实现STL:前言
    Effective C++学习笔记 条款07:为多态基类声明virtual析构函数
    Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝
    Effective C++学习笔记 条款05:了解C++默默编写并调用的哪些函数
    Effective C++学习笔记 条款04:确定对象被使用前已先被初始化
    Effective C++学习笔记 条款02:尽量以const,enum,inline替换 #define
  • 原文地址:https://www.cnblogs.com/DReamLion/p/14916206.html
Copyright © 2011-2022 走看看