zoukankan      html  css  js  c++  java
  • 题解 洛谷P6185 6186 6187 NOI Online(全)

    洛谷P6185 [NOI Online 提高组]序列

    问题相当于有一个序列(c_i=a_i-b_i),要进行若干次操作使得(c_i)每一项都为(0)

    有一档(t=2)的部分分,说明(2)肯定是比(1)好处理的。不妨先考虑只有(2)的情况。对于操作(2 u v),我们在(u,v)之间连一条无向边。这样整张图形成了若干个连通块。不难发现在每个连通块内部,对于任意两个节点(u,v),我们都可以实现(u exttt{++},v exttt{--})(u exttt{--},v exttt{++})。也就是说在每个连通块内部,节点的权值可以相互转运。那么只有(2)操作时,有解当且仅当每个连通块内的权值和都是(0)

    在既有(1)也有(2)时,先用同样的方法把(n)个节点缩成若干个连通块,每个连通块的权值就是其中所有节点的权值和。这样,每个(1)操作就转化为了连通块之间的操作(特别地,当(1)操作的两个节点在同一连通块内时,这次操作的效果相当于让这个连通块的权值(pm2))。这样,把每个连通块看做一个点,则原问题转化为了对一个新序列,只有(1)操作的情况。

    对于一次操作(1 u v),我们在(u,v)之间连一条无向边。发现连通块之间相互独立,因此全局有解当且仅当每个连通块都有解。考虑一个连通块。因为一次(1)操作会造成这个连通块内的权值和(pm2),因此有解的一个必要条件是连通块内所有节点的权值和为偶数

    考虑两个节点之间,若存在一条路径(可以有重边/自环)长度为偶数,则这两个节点的权值可以实现相互转运。如果一个连通块内存在奇环,则任意两个节点的距离都可以是偶数,此时该连通块有解。否则这个连通块是一个二分图。我们对它黑白染色后,每条边一定连接两个不同颜色的节点。发现我们一次(1)操作可以让黑、白点的权值和同时(+1)或同时(-1),且同种颜色的节点之间距离一定是偶数。因此此时有解当且仅当黑、白点的权值和相等

    时间复杂度(O(n))

    参考代码:

    //problem:P6185
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fst first
    #define scd second
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    namespace Fread{
    const int MAXN=1<<20;
    char buf[MAXN],*S,*T;
    inline char getchar(){
    	if(S==T){
    		T=(S=buf)+fread(buf,1,MAXN,stdin);
    		if(S==T)return EOF;
    	}
    	return *S++;
    }
    }//namespace Fread
    #ifdef ONLINE_JUDGE
    	#define getchar Fread::getchar
    #endif
    inline int read(){
    	int f=1,x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    inline ll readll(){
    	ll f=1,x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=1e5;
    int n,m,a[MAXN+5],fa[MAXN+5],cnt_e,cnt_id,id[MAXN+5],col[MAXN+5];
    pii e[MAXN+5];
    int get_fa(int x){return x==fa[x]?x:(fa[x]=get_fa(fa[x]));}
    vector<int>G[MAXN+5];
    ll val[MAXN+5],Sum,sum1,sum2;
    bool flag;
    void dfs(int u){
    	if(col[u]==1)sum1+=val[u];else sum2+=val[u];
    	Sum+=val[u];
    	for(int i=0;i<(int)G[u].size();++i){
    		int v=G[u][i];
    		if(col[v]){
    			if(col[v]!=3-col[u])flag=0;
    		}else{
    			col[v]=3-col[u];dfs(v);
    		}
    	}
    }
    int main() {
    	int Testcases=read();while(Testcases--){
    		n=read();m=read();
    		for(int i=1;i<=n;++i)fa[i]=i,a[i]=read();
    		for(int i=1;i<=n;++i)a[i]-=read();
    		cnt_e=0;
    		for(int i=1;i<=m;++i){
    			int t=read(),u=read(),v=read();
    			if(t==1){
    				e[++cnt_e]=mk(u,v);
    			}else{
    				u=get_fa(u),v=get_fa(v);
    				if(u!=v)fa[u]=v;
    			}
    		}
    		cnt_id=0;
    		for(int i=1;i<=n;++i)if(get_fa(i)==i)id[i]=++cnt_id;
    		for(int i=1;i<=cnt_id;++i)vector<int>().swap(G[i]),col[i]=0,val[i]=0;
    		for(int i=1;i<=n;++i)val[id[get_fa(i)]]+=a[i];
    		for(int i=1;i<=cnt_e;++i){
    			int u=id[get_fa(e[i].fst)],v=id[get_fa(e[i].scd)];
    			G[u].pb(v);G[v].pb(u);
    		}
    		bool yes=1;
    		for(int i=1;i<=cnt_id;++i)if(!col[i]){
    			flag=1;Sum=sum1=sum2=0;
    			col[i]=1;dfs(i);
    			if(Sum%2!=0){
    				yes=0;
    				break;
    			}
    			if(flag&&sum1!=sum2){//是二分图
    				yes=0;
    				break;
    			}
    		}
    		if(yes)puts("YES");else puts("NO");
    	}
    	return 0;
    }
    

    洛谷P6186 [NOI Online 提高组]冒泡排序

    考虑一轮冒泡排序会对序列产生什么影响。设位置(i)前面值大于(a_i)的位置数量为(s_i)。则一轮冒泡排序后,首先所有(s_i)向左移一位,然后所有大于(0)(s_i)都减(1)。即:(s_i=max(s_{i+1}-1,0))

    维护两个树状数组。下标均为(s_i)的值域(显然所有(s_i<n))。第一个树状数组存(s_j=i)(j)的个数,第二个树状数组存(s_j=i)(s_j)之和。交换操作相当于是在树状数组上做简单的单点修改。查询时,显然只有(s_i>k)的这些位置会影响答案(其他位置减(k)次之后取(max)后就都变为(0)了)。用第二个树状数组求后缀和,减去第一个树状数组的后缀和乘以(k),就是答案了。

    时间复杂度(O(mlog n))

    参考代码:

    //problem:P6186
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fst first
    #define scd second
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    namespace Fread{
    const int MAXN=1<<20;
    char buf[MAXN],*S,*T;
    inline char getchar(){
    	if(S==T){
    		T=(S=buf)+fread(buf,1,MAXN,stdin);
    		if(S==T)return EOF;
    	}
    	return *S++;
    }
    }//namespace Fread
    #ifdef ONLINE_JUDGE
    	#define getchar Fread::getchar
    #endif
    inline int read(){
    	int f=1,x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    inline ll readll(){
    	ll f=1,x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=2e5;
    int n,m,a[MAXN+5],s[MAXN+5];
    struct FenwickTree{
    	ll c[MAXN+5];
    	void modify(int p,int v){
    		if(!p)return;
    		for(;p<=n;p+=(p&(-p)))c[p]+=v;
    	}
    	ll query(int p){
    		ll res=0;
    		for(;p;p-=(p&(-p)))res+=c[p];
    		return res;
    	}
    	void clr(){
    		memset(c,0,sizeof(c));
    	}
    	FenwickTree(){}
    }T,T1;
    int main() {
    	n=read();m=read();
    	for(int i=1;i<=n;++i)a[i]=read(),s[i]=T.query(n)-T.query(a[i]),T.modify(a[i],1);
    	T.clr();
    	for(int i=1;i<=n;++i)T.modify(s[i],1),T1.modify(s[i],s[i]);
    	while(m--){
    		int op=read(),x=read();
    		if(op==1){
    			T.modify(s[x],-1);T.modify(s[x+1],-1);
    			T1.modify(s[x],-s[x]);T1.modify(s[x+1],-s[x+1]);
    			int t2=s[x]+(a[x+1]>a[x]);
    			int t1=s[x+1]-(a[x]>a[x+1]);
    			s[x]=t1;s[x+1]=t2;
    			swap(a[x],a[x+1]);
    			T.modify(s[x],1);T.modify(s[x+1],1);
    			T1.modify(s[x],s[x]);T1.modify(s[x+1],s[x+1]);
    			//cout<<"* ";for(int i=1;i<=n;++i)cout<<s[i]<<" ";cout<<endl;
    		}else{
    			if(x>=n){printf("%d
    ",0);continue;}
    			ll res=T1.query(n)-T1.query(x)-(ll)x*(T.query(n)-T.query(x));
    			printf("%lld
    ",res);
    		}
    	}
    	return 0;
    }
    

    洛谷P6187 [NOI Online 提高组]最小环

    观察样例,猜测构造策略。

    首先,距离为(k)的位置一定构成了若干个长度相等的环,每个环上相邻两个点距离都是(k),环与环之间不存在距离为(k)的点。因此每个环是独立的。容易发现,每个环的长度都是(frac{n}{gcd(n,k)})

    原则上我们要尽量把大的数放在一起,这样才能使乘积之和最大化。

    因此如果环长为(len),则一定把前(len)大的数放在同一个环中,接下来(len)个数放下一个环中,以此类推......。

    对于一个环,设它上面的数字分别为(s_1,dots,s_{len}),则它对答案的贡献是(sum_{i=1}^{len}s_is_{imod len+1})。我们任选一个位置放最大的数,然后在它两边分别放次大的和更次大的数。例如,(len=5)时的最优放法为:((2,4,5,3,1))

    因为可能的环长一定是(n)的约数,我们预处理每个(len)的答案。总时间复杂度(O(nsqrt{n}))

    参考代码:

    //problem:P6187
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fst first
    #define scd second
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    namespace Fread{
    const int MAXN=1<<20;
    char buf[MAXN],*S,*T;
    inline char getchar(){
    	if(S==T){
    		T=(S=buf)+fread(buf,1,MAXN,stdin);
    		if(S==T)return EOF;
    	}
    	return *S++;
    }
    }//namespace Fread
    #ifdef ONLINE_JUDGE
    	#define getchar Fread::getchar
    #endif
    inline int read(){
    	int f=1,x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    inline ll readll(){
    	ll f=1,x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=2e5;
    int n,m,a[MAXN+5],s[MAXN+5],len;
    ll ans[MAXN+5];
    inline int pre(int i){return i==1?len:i-1;}
    inline int nxt(int i){return i==len?1:i+1;}
    inline int gcd(int x,int y){return !y?x:gcd(y,x%y);}
    int main() {
    	n=read();m=read();
    	for(int i=1;i<=n;++i)a[i]=read();
    	sort(a+1,a+n+1);
    	for(len=1;len<=n;++len)if(n%len==0){
    		//环长:len
    		//cout<<"len "<<len<<endl;
    		int cur=n;
    		while(cur){
    			int l=1,r=1;s[1]=a[cur--];
    			for(int i=2;i<=len;++i){
    				if(i&1)l=pre(l),s[l]=a[cur--];
    				else r=nxt(r),s[r]=a[cur--];
    			}
    			//for(int i=1;i<=len;++i)cout<<s[i]<<" ";cout<<endl;
    			for(int i=1;i<=len;++i)ans[len]+=(ll)s[i]*s[nxt(i)];
    		}
    	}
    	while(m--){
    		int k=read();
    		int len=n/gcd(n,k);
    		printf("%lld
    ",ans[len]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    svn 指定不更新目录
    C# 修改win环境变量 来加载dll库
    基本组件
    在SD卡上创建/删除文件夹 使用DDMS透视图管理SD卡
    linux中图形界面改成文本
    表格布局和线性布局
    文件操作
    静态库的生成和调用
    船载电子海图系统(E C S )概述
    GPS全球定位系统构成及原理
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12445083.html
Copyright © 2011-2022 走看看