• 绵阳东辰国际test201909.28


    老张原话:"这套题比较切合noip难度";我笑了

    数据为n<=1e5,ai<=1e18,k<=3!!!!!!

    以下是考场时乱搞的代码,写的垃圾75分,没耐心的可以不用看跳过

    #include<bits/stdc++.h>
    #define ll long long
    #define il inline
    #define ri register int
    using namespace std;
    const int maxn=1e5+5;
    int n,k,len;
    ll a[maxn];
    bool vis[maxn];
    il bool cmp(ll a,ll b){return a>b;}
    il int find(int l,int r,ll x){
    	int mid,ans,prel=l;ans=l;
    	if(r<l)return -1;
    	while(l<=r){
    		mid=l+r>>1;
    		if(a[mid]>x)ans=mid,l=mid+1;
    		else r=mid-1;
    	} 
    	if(a[ans]<=x)ans--;
    	while(ans>=prel){
    		if(vis[ans])ans--;
    		else break;
    	}
    	if(ans>=prel)return ans;return -1;
    }
    il ll check(int l,int r){ll res=0;
       for(ri i=l;i<=r;i++)res+=a[i],vis[i]=false;
       int rr;
       for(ri i=l;i<=r-2;i++){
       	if(vis[i])continue;rr=i+1;
       	while(vis[rr])rr++;
       	int t=find(rr+1,r,a[i]-a[rr]);
       	if(t!=-1){vis[t]=true;vis[rr]=true;i++;}
       	else return -1;
       }return res;
    }
    il int find2(int l,int r,ll x){
    for(ri i=l;i<=r;i++)
    if(a[i]>x)return i;
    return -1;
    }
    il ll check2(int l,int r){ll res=0;
       for(ri i=l;i<=r;i++)res+=a[i],vis[i]=false;
       int rr;
       for(ri i=l;i<=r-2;i++){
       	if(vis[i])continue;rr=i+1;
       	while(vis[rr])rr++;
       	int t=find2(rr+1,r,a[i]-a[rr]);
       	if(t!=-1){vis[t]=true;vis[rr]=true;i++;}
       	else return -1;
       }return res;
    }
    int main(){
    	freopen("triangle.in","r",stdin);
    	freopen("triangle.out","w",stdout);
    	scanf("%d%d",&n,&k);len=k*3;
    	for(ri i=1;i<=n;i++)scanf("%lld",&a[i]);
    	sort(a+1,a+1+n,cmp);
    	if(k==1){
    		for(ri i=1;i+len-1<=n;i++){
    		ll ans=check(i,i+len-1);
    		if(ans!=-1){printf("%lld
    ",ans);return 0;}
    	    else continue;
    	}
    	printf("-1
    ");
    	}
    	else {
    		ll ans;
    		for(ri i=1;i+len-1<=n;i++){
    		 ans=check(i,i+len-1);
    		if(ans==-1)continue;
    		else break;
    	}
    	if(ans==-1)
    		for(ri i=1;i+len-1<=n;i++){
    		ans=check2(i,i+len-1);
    		if(ans==-1)continue;
    		else break;
    	}
    	if(ans!=-1)printf("%lld
    ",ans);
    	else printf("-1
    ");}
    	return 0;
    }
    

    考虑到k<=3,应从小数据进行突破

    首先排序是肯定的(从大到小或从小到大都行)

    这里就说从大到小

    一:k==1

    这种情况只有可能三条边排完序后是相连的三条(当然在保证能形成三角形的前提下)

    怎么理解呢?假如a>b>c>d

    如果a+b>c 显然a+b>d

    但因为c>d 那么选d就没选 c 优秀

    这样线性扫过去判断就可以

    二:k==2

    虽然最优解不一定是连续的6条边

    但是如果将其中一个三 角形的攳条边删掉,那么第2个三角形在剩下的n−1条边中一定是连续的。

    因此最优解要么是连续的6条边,要么是两组连续的3条边。对于连续的6条边我们枚举所 有组合的情况即可,

    对于连续的3条边我们可以从大到小进行贪心。

    三;k==3

    k1的k2情况类似,只是更加复杂一些

    最优解共4种情况:

    情况1 连续9条边

    情况2 连续6条长边和连续3条短边

    情况3 连续3条长边和连续6条短边

    情况4 3组连续的3条边

    结合一下就行了

    代码比较复杂,肯定都不想看于是就算了吧,

    我知道你也不想读题,下面有题目大意

    第一题耗时过大,于是此题基本没啥时间了

    题目大意
    有一幅图,n个点,m条边,边有边权。

    有三种操作:加边,删边,询问把图划分为两个点 集后两个端点属于同一个点集的边的最大值的最小值。

    感觉特别眼熟?

    https://www.luogu.org/problem/P1525

    每次询问将当前存在的边排序后,从大到小依次加入,若加入一条边后出现奇环,则这条边就是答案

    那道题没有修改操作,就简单多了,直接并查集维护即可

    code:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    struct NODE
    {
        int x,y,val;//一个状态,需要保存3个量
    };
    struct cmp
    {
        bool operator()(const NODE& a,const NODE& b)//重载运算符
        {
            return a.val<b.val;//重载
        }
    };
    priority_queue<NODE,vector<NODE>,cmp>gx;//大根堆(对于val)
    int bcj[100000];//并查集
    int find_(int node)//并查集找根节点过程
    {
        if(bcj[node]!=node)return bcj[node]=find_(bcj[node]);
        return node;
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int dr1,dr2,zhi;
        for(int i=1;i<=2*n;i++)bcj[i]=i;//初始化,记得是2*n
        while(m--)
        {
            scanf("%d%d%d",&dr1,&dr2,&zhi);
            gx.push((NODE){dr1,dr2,zhi});//加入队列
        }
        while(!gx.empty())//一直到没有冲突
        {
            if(find_(gx.top().x)==find_(gx.top().y))//如果在同一并查集内
            {
                printf("%d",gx.top().val);//直接输出
                return 0;
            }
            bcj[bcj[gx.top().x]]=bcj[find_(gx.top().y+n)];
            bcj[bcj[gx.top().y]]=bcj[find_(gx.top().x+n)];//维护新的关系,记得有两个并查集要维护
            gx.pop();
        }
        printf("%d",0);//特判0的情况
        return 0;
    }
    

    考虑有修改操作

    part one如果还是向原来一样的话直接模拟50分是可以拿到的

    可以发现,那些加进去只产生偶环的边是没有用的。

    因此只 有O(n)条边是有用的(即不产生环的边和第一次产生奇环的边)。

    考虑线段树分治,把每条边按其存在时间的区间加入到线段树中。

    对线段树每个节点,会 有许多条边。

    预处理出每个节点上有用的O(n)条边。

    然后每次询问就是把O(logn)个节点上的 信息合并。

    令询问次数为Q。

    由于可以将询问看作时间点建线段树,那么线段树中只有O(Q)个节点。

    每个节点的信息可以由其父亲得来

    code by std

    #include<bits/stdc++.h>
    using namespace std;
    const int MAX_N=5+1e3;
    int n;
    struct DSF{
    	int fa[MAX_N],p[MAX_N];
    	void make_set(int x){ fa[x]=x,p[x]=0; }
    	int find_set(int x){ 
    		if(fa[x]!=x){
    			int y=fa[x];
    			fa[x]=find_set(fa[x]);
    			p[x]^=p[y];
    		}
    		return fa[x];
    	}
    	bool merge(int x,int y){
    		if(find_set(x)==find_set(y))
    			return p[x]^p[y];
    		int u=find_set(x),v=find_set(y);
    		p[u]=p[x]^p[y]^1;
    		fa[u]=v;
    		return true;
    	}
    	bool member(int x,int y){
    		return find_set(x)==find_set(y);
    	}
    }dsf;
    struct E{ int x,y,k; };
    struct Q{ E x; int l,r; };
    inline bool operator<(E a,E b){ return a.k>b.k; }
    vector<Q> e;
    struct SEG{
    	vector<E> tree[MAX_N<<2];
    	void build(int p,int l,int r){
    		tree[p].clear(); 
    		if(l==r) return;
    		int mid=l+r>>1;
    		build(p+p,l,mid);
    		build(p+p+1,mid+1,r);
    	}
    	void change(int p,int l,int r,int x,int y,E key){
    		if(l==x&&r==y){
    			tree[p].push_back(key);
    			return;
    		}
    		int mid=l+r>>1;
    		if(y<=mid) return change(p+p,l,mid,x,y,key);
    		else if(x>mid) return change(p+p+1,mid+1,r,x,y,key);
    		else change(p+p,l,mid,x,mid,key),change(p+p+1,mid+1,r,mid+1,y,key);
    	}
    	void dfs(int p,int l,int r,vector<E> k,int ans){	
    		for(int i=0;i<tree[p].size();++i)
    			k.push_back(tree[p][i]);
    		sort(k.begin(),k.end());
    		for(int i=1;i<=n;++i) dsf.make_set(i);
    		for(int i=0;i<k.size();++i){ 
    			if(!dsf.member(k[i].x,k[i].y)){
    				dsf.merge(k[i].x,k[i].y);
    			}else{
    				if(!dsf.merge(k[i].x,k[i].y)){
    					ans=max(ans,k[i].k);
    					k.resize(i); break;
    				}
    			}
    		}
    		if(l==r){
    			printf("%d
    ",ans);
    			return;
    		}
    		int mid=l+r>>1;
    		dfs(p+p,l,mid,k,ans);
    		dfs(p+p+1,mid+1,r,k,ans);
    	}
    }seg;
    int main(){
    	freopen("redgreen.in","r",stdin);
    	freopen("redgreen.out","w",stdout);
    	int m,q; scanf("%d%d%d",&n,&m,&q);
    	e.resize(m+1); 
    	for(int i=1;i<=m;++i){ 
    		scanf("%d%d%d",&e[i].x.x,&e[i].x.y,&e[i].x.k);
    		e[i].l=1; e[i].r=-1;
    	}
    	vector<E> k; for(int i=1;i<=m;++i) k.push_back(e[i].x);
    	sort(k.begin(),k.end());
    	for(int i=1;i<=n;++i) dsf.make_set(i);
    	int top=1;
    	for(int i=1;i<=q;++i){
    		char c=getchar();
    		while(c<'A'||c>'Z') c=getchar();
    		if(c=='D'){
    			int x; scanf("%d",&x);
    			e[x].r=top-1;
    		}else if(c=='A'){
    			e.push_back((Q){{0,0,0},0,0}); ++m;
    			scanf("%d%d%d",&e[m].x.x,&e[m].x.y,&e[m].x.k);
    			e[m].l=top; e[m].r=-1;
    		}else{
    			++top;
    		}
    	}
    	for(int i=1;i<=m;++i)
    		if(e[i].r==-1) e[i].r=top-1;
    	--top;
    	seg.build(1,1,top);
    	for(int i=1;i<=m;++i)
    		if(e[i].l<=e[i].r)
    			seg.change(1,1,top,e[i].l,e[i].r,e[i].x);
    	seg.dfs(1,1,top,vector<E>(),0);
    	return 0;
    }
    

    T3是道

    期望题

    不会

  • 相关阅读:
    XML与JSON的对比
    WPF dataGrid下的ComboBox的绑定
    WPF 导出Excel(合并单元格)
    WPF x:key和x:name用法
    WPF Canvas
    WPF Visibility属性用法
    AxInterop.ShockwaveFlashObjects.dll 问题
    BadImageFormatException,未能加载正确的程序集XXX的解决办法
    inner join ,left join ,right join区别
    Mac系统下php.ini的位置
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11603490.html
Copyright © 2020-2023  润新知