• 【简要题解】CF 737 Div2 简要题解


    【简要题解】CF 737 Div2 简要题解

    退役一年之后还能上分?

    A - Ezzat and Two Subsequences

    首先(O(n))枚举分成的两个序列的大小分别是多少假设分别是(x)(n-x)

    数学直觉告诉我,小的和小的一起,大的和大的一起,所以我们把前(x)大放在一起,剩下的放在一起就行

    题解给了证明:

    The average of a group of numbers always has a value between the minimum and maximum numbers in that group.

    这就很显然了

    //@winlere
    #include<bits/stdc++.h>
    using namespace std;
    int qr(){
    	int ret=0,c=getchar(),f=0;
    	while(!isdigit(c)) f=c=='-',c=getchar();
    	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    const int maxn=1e5+5;
    double data[maxn],pre[maxn],suf[maxn];
    
    int main(){
    	ios::sync_with_stdio(0);
    	int T=qr();
    	while(T--){
    		int n=qr();
    		for(int t=1;t<=n;++t) data[t]=qr();
    		sort(data+1,data+n+1);
    		for(int t=1;t<=n;++t) pre[t]=pre[t-1]+data[t];
    		suf[n+1]=0;
    		for(int t=n;t;--t) suf[t]=suf[t+1]+data[t];
    		double ans=-2e9;
    		for(int t=1;t<n;++t) ans=max(ans,pre[t]/t+suf[t+1]/(n-t));
    		cout<<fixed<<setprecision(10)<<ans<<endl;
    	}
    	return 0;
    }
    
    
    

    B - Moamen and k-subarrays

    离散化后直接看非连续段的个数,很难说比A题难

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int qr(){
    	int ret=0,c=getchar(),f=0;
    	while(!isdigit(c)) f=c=='-',c=getchar();
    	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    const int maxn=1e5+5;
    int data[maxn],sav[maxn];
    
    int main(){
    	int T=qr();
    	while(T--){
    		int n=qr(),k=qr(),cnt=0;
    		for(int t=1;t<=n;++t) sav[t]=data[t]=qr();
    		sort(sav+1,sav+n+1);
    		for(int t=1;t<=n;++t) data[t]=lower_bound(sav+1,sav+n+1,data[t])-sav;
    		data[n+1]=-114514;
    		for(int t=1,r=1;t<=n;t=++r){
    			while(data[r+1]==data[r]+1) ++r;
    			++cnt;
    		}
    		puts(cnt<=k?"YES":"NO");
    	}
    	return 0;
    }
    
    
    

    C - Moamen and XOR

    这道题深刻地揭示了退役多年的我已经变得多么菜了

    从二进制高位到低位考虑问题的,高位比较出结果低位随便填了。

    (f(n))表示有(n)(bit),这些(bit)的异或和(=)并起来的和

    (g(n))表示有(n)(bit),这些(bit)异或和(>)并起来的和

    显然:

    [f(n)=cases {2^{n-1}-1 & 2|n\2^{n-1}+1 & otherwise } \ g(n)=cases {1 &2|n \ 0 & otherwise} ]

    只需要知道(2^{n-1})是怎么来的

    (n)个二进制位中,选中奇数个使其为一的方案数=选中偶数个使其为一的。这个结论的证明是,可以构造一一对应的关系:将这些二进制位中的第一个给翻转一下得到偶与奇的对应。

    其余的(+1,-1)是考虑全(1)或全(0)的情况

    然后枚举是再哪一位比出个(>)​的结果出来。注意到因为原题是(ge)​,所以还要加一个全部位都相等的情况。

    还要注意(k=0)

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int qr(){
    	int ret=0,c=getchar(),f=0;
    	while(!isdigit(c)) f=c=='-',c=getchar();
    	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    const int mod=1e9+7;
    const int maxn=2e5+5;
    typedef long long ll;
    int f[maxn],g[maxn];
    int MOD(const int&a,const int&b){return 1ll*a*b%mod;}
    int MOD(const int&a){return a%mod;}
    int MOD(vector<int> ve){int ret=1;for(auto t:ve) ret=MOD(ret,t); return ret;}
    
    int ksm(const int&ba,const ll&p){
    	int ret=1;
    	for(ll t=p,b=ba;t;t>>=1,b=MOD(b,b))
    		if(t&1) ret=MOD(ret,b);
    	return ret;
    }
    
    void pre(int n){
    	f[1]=2; 
    	for(int t=2;t<=n;++t)
    		if(t&1)	f[t]=ksm(2,t-1)+1;
    		else f[t]=ksm(2,t-1)-1,g[t]=1;
    }
    
    int main(){
    	ios::sync_with_stdio(0);
    	pre(2e5);
    	int T=qr();
    	while(T--){
    		int n=qr(),k=qr(),ans=0;
    		if(k) {
    			for(int t=k;t;--t){
    				int ret=MOD({ksm(f[n],k-t),g[n],ksm(2,1ll*(t-1)*n)});
    				ans=MOD(ans+ret);
    			}
    			ans=MOD(ans+ksm(f[n],k));
    		}else ans=1;
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    
    
    

    D - Ezzat and Grid

    线段树都写错,我不配叫OIer((

    问题转化为图论问题,将满足相邻条件的两行由序数小的向序数大的连一条有向边,问题转化为最长路问题

    然而这个复杂度是(O(n^2))的,考虑优化,首先一个问题,如果(1->2)合理,并且(2->3)合理,并且(1->3)合理,那么我们不需要连(1->3)了。

    考虑什么时候(1->2,2->3,1->3)都合理,当且仅当他们有某个相同位置都有"1"

    离散化所有位置信息,用线段树维护"在这个位置有'1'的编号最大的行的编号",这个用线段树轻松维护。

    这个时候就有人问了,你修改的时候可以用lazy tag复杂度是对的,但是你查询的时候复杂度不是(O(值域))了吗

    并非如此,每个我们可以记录一个(datin[0,n])表示当前区间是全为一个数,若有则这个数是多少。

    考虑那些(dat=0)​的节点,这是因为某个位置的编号和周围不一样,这个打破统一的情况只会在修改区间的端点附近产生(这个最开始我没考虑到,之前居然分析成(O(log^2m))我真的太菜了...),可以发现一次修改操作最多产生$ log m$​个这样的区间

    而我们查询的时候,每次遇到(dat),我们就递归下去,而且因为修改的区间和查询的区间一样,这个时候(dat)更新了,所以相当于查到一个(dat=0)就把这个(dat)修改成有值了。

    复杂度(O(mlog m))

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<vector>
    
    using namespace std;
    int qr(){
    	int ret=0,c=getchar(),f=0;
    	while(!isdigit(c)) f=c=='-',c=getchar();
    	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    const int maxn=3e5+5;
    vector<int> e[maxn];
    int n,m,sav[maxn<<1],cnt,dp[maxn],usd[maxn];
    
    void add(int fr,int to){
    	if(fr<=0||to<=0||to==fr) return;
    	e[fr].push_back(to);
    }
    
    struct QWQ{
    	int i,l,r;
    	bool operator < (const QWQ&a)const{
    		return i<a.i;
    	}
    }data[maxn];
    
    #define mid ((l+r)>>1)
    #define lef l,mid,pos<<1
    #define rgt mid+1,r,pos<<1|1
    
    struct XDS{
    	int tag,dat;//删你麻痹的uni去死吧!
    	void upd(int x){tag=dat=x;}
    }seg[maxn<<4];
    
    void pd(int pos){
    	if(!seg[pos].tag) return;
    	seg[pos<<1].upd(seg[pos].tag);
    	seg[pos<<1|1].upd(seg[pos].tag);
    	seg[pos].tag=0;
    }
    
    void pp(int pos){
    	if(seg[pos<<1].dat==seg[pos<<1|1].dat&&seg[pos<<1].dat>0)
    		seg[pos].dat=seg[pos<<1].dat;
    	else seg[pos].dat=0;
    }
    
    void que(int to,int L,int R,int l,int r,int pos){
    	if(L>r||R<l) return;
    	if(seg[pos].dat||l==r) return add(seg[pos].dat,to);
    	pd(pos);
    	que(to,L,R,lef); que(to,L,R,rgt);//线段树都不会写,逻辑发生巨大错误,upd的时候不管了。。。。写下代码的时候要想想啊!
    	pp(pos);
    }
    
    void upd(int to,int L,int R,int l,int r,int pos){
    	if(L>r||R<l) return;
    	if(L<=l&&r<=R) return seg[pos].upd(to);
    	pd(pos);
    	upd(to,L,R,lef); upd(to,L,R,rgt);
    	pp(pos);
    }
    
    void dfs(int now){
    	if(dp[now]) return;
    	dp[now]=1;
    	for(auto t:e[now])
    		if(t^now)
    			dfs(t),dp[now]=max(dp[now],dp[t]+1);
    }
    
    void getAns(int now){
    	usd[now]=1;
    	for(auto t:e[now])
    		if(dp[t]+1==dp[now])
    			return getAns(t);
    }
    
    int main(){
    	n=qr(); m=qr();
    	for(int t=1;t<=m;++t) data[t].i=qr(),sav[++cnt]=data[t].l=qr(),sav[++cnt]=data[t].r=qr();
    	sort(sav+1,sav+cnt+1);
    	cnt=unique(sav+1,sav+cnt+1)-sav-1;
    	for(int t=1;t<=m;++t) data[t].l=lower_bound(sav+1,sav+cnt+1,data[t].l)-sav,data[t].r=lower_bound(sav+1,sav+cnt+1,data[t].r)-sav;
    	sort(data+1,data+m+1);
    	for(int t=1;t<=m;++t)
    		que(data[t].i,data[t].l,data[t].r,1,cnt,1),upd(data[t].i,data[t].l,data[t].r,1,cnt,1);
    	for(int t=1;t<=n;++t) dfs(t);
    	int ans=0;
    	for(int t=1;t<=n;++t) ans=max(ans,dp[t]);
    	printf("%d
    ",n-ans);
    	for(int t=1;t<=n;++t)
    		if(dp[t]==ans)
    			getAns(t),t=n;
    	for(int t=1;t<=n;++t)
    		if(!usd[t])
    			printf("%d ",t);
    	if(n-ans) putchar('
    ');
    	return 0;
    }
    
    
    
    博客保留所有权利,谢绝学步园、码迷等不在文首明显处显著标明转载来源的任何个人或组织进行转载!其他文明转载授权且欢迎!
  • 相关阅读:
    Mac下安装cscope和ctags
    MAC的VIMRC
    我的Linux系统的VIMRC
    关于在VI中查看BIN文件二进制值不对的问题
    没有关心过编写代码方式的好处,你是不是也是这样?
    四十不惑,真的不惑了么?
    jQuery通过text值来设置选定,以及遍历select的选项个数和遍历
    C#解析XML文件
    网页调用本地程序(Windows下浏览器全兼容)
    C#获取文件的md5
  • 原文地址:https://www.cnblogs.com/winlere/p/15130884.html
Copyright © 2020-2023  润新知