zoukankan      html  css  js  c++  java
  • APIO2015简要题解

    这场比赛当初是67(?)反正就是Cu滚粗了……

    先给个题目的传送门:http://wenku.baidu.com/link?url=mUxdsYomenU-e9SFVPacVtXysemiQA4KnP1EldVuYaB8ECiaLQN4VIAEc19MmHQWyopKLxhzWjWdIE_QrxDWi6PVQt4YuI8IYqPRBN8HUbu

    T1

    题目大意:给你一个序列,让你分成x段,x∈[A,B],要求每段之和的or和最小,输出最小的or和。A=1时序列长度n<=2000,A>=1时序列长度n<=100

    和位运算相关的这类题大多都是按位DP

    从高位做到低位,如果该位可以是0,那么就设为0,否则就置1然后随它去

    具体实现用dp

    如果A=1,直接用dp[i]记录[1,i]区间内,最少分几个块,随便搞搞O(n^2)转移就出来了,如果dp[n]<=B,这位就可以是0,复杂度O(n^2logn)

    如果A>1,不能直接记录最少分几个区间,要把切所有刀的可行性都记下来(原谅我的语言表达……),用dp[i][j]表示[1,i]区间能否内分j块,如果有x∈[A,B]使dp[n][x]=1,这位就可以是0,复杂度O(n^3logn)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    #define ll long long
    #define N 2003
    #define INF 2003
    
    using namespace std;
    inline int read(){
    	int ret=0;char ch=getchar();
    	while (ch<'0' || ch>'9') ch=getchar();
    	while ('0'<=ch && ch<='9'){
    		ret=ret*10-48+ch;
    		ch=getchar();
    	}
    	return ret;
    }
    
    ll bin[60];int K;
    
    int n,L,R;
    ll s[N];
    
    bool f[105][105];
    int dp[N];
    
    int main(){
    	n=read();L=read();R=read();s[0]=0;
    	for (int i=1;i<=n;++i) s[i]=s[i-1]+read();
    	for (bin[K=0]=1;bin[K]<=s[n];++K) bin[K+1]=bin[K]<<1;
    	--K;
    	ll ans=0;
    	if (L==1) for (;K>=0;--K){
    		memset(dp,127,sizeof(dp));dp[0]=0;
    		ans=ans<<1;
    		for (int i=1;i<=n;++i){
    			for (int pre=0;pre<i;++pre)
    				if ((((s[i]-s[pre])>>K)|ans)==ans)
    					dp[i]=min(dp[i],dp[pre]+1);
    		}
    		ans^=(ll)(dp[n]>R);
    	}
    	else for (;K>=0;--K){
    		memset(f,0,sizeof(f));f[0][0]=1;
    		ans=ans<<1;
    		for (int i=1;i<=n;++i)
    			for (int j=1;j<=R;++j)
    				for (int pre=0;pre<i;++pre)
    					if (f[pre][j-1]&&(((s[i]-s[pre])>>K)|ans)==ans){
    						f[i][j]=1;break;
    					}
    		for (int i=L;i<=R;++i) ans|=(ll)f[n][i];
    		ans^=1LL;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

     

    T2

    题目大意:给你一条链,每个点上有狗,每条狗都有一个跳跃力,表示它跳一步能跳过几个节点。现在一条狗要传递消息给另一条狗,只有持有消息的狗才能跳,问所有狗总共最少跳几步。链长狗数均为3*10^4

    据说现场暴力出奇迹…………excited

    以下是正解

    对于跳跃力小于sqrt(n)的狗(我把它叫做小狗),按照跳跃力建立sqrt(n)个类似【轨道】(?)(原谅我形象的比喻……)的东西。每个轨道包含n个点,对应原链的每个节点。对应跳跃力为j的轨道上的点向同轨道左&右边第j个点分别连边,权为1。然后每个轨道上的点都向原链上它对应的点连边,权为0。

    对于跳跃力大于sqrt(n)的狗(我把它叫做大狗),暴力简图就行,向所有它跳的到的原链上的点连边,边权为跳的步数。

    对于原链上的每个点,向每个初始位置在它上面的狗对应的节点连边,权都为0,如果是大狗,就连到这条狗对应的点,如果是小狗,就连到它跳跃力的轨道上当前点对应的点。总结来说,原链的点,就是用来换狗的。

    然后跑dijkstra,边和点都是nlogn的。有个细节就是,边我没有预处理建,我直接在dijkstra里找了,感觉这样会好写一点?

    说的有点乱……详见代码吧

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #define N 5000006
    
    using namespace std;
    inline int read(){
    	int ret=0;char ch=getchar();
    	while (ch<'0' || ch>'9') ch=getchar();
    	while ('0'<=ch && ch<='9'){
    		ret=ret*10-48+ch;
    		ch=getchar();
    	}
    	return ret;
    }
    const int K=150;
    
    struct edge{
    	int adj,next;
    	edge(){}
    	edge(int _adj,int _next):adj(_adj),next(_next){}
    } e[30004];
    int n,g[30004],m;
    void AddEdge(int u,int v){
    	e[++m]=edge(v,g[u]);g[u]=m;
    }
    
    int l,doge;
    int id[30004],cnt;
    int pos[30004],jump[30004];
    int code[30004];
    
    struct HeapNode{
    	int pos,value;
    	HeapNode(){}
    	HeapNode(int _pos,int _value):pos(_pos),value(_value){}
    };
    inline bool operator >(const HeapNode &x,const HeapNode &y){
    	return x.value>y.value;
    }
    
    priority_queue<HeapNode,vector<HeapNode> ,greater<HeapNode> > h;
    bool flag[N];
    int mind[N];
    void dijkstra(int _s,int _t){
    	memset(mind,127,sizeof(mind));
    	memset(flag,0,sizeof(flag));
    	while (!h.empty()) h.pop();
    	flag[_s]=1;mind[_s]=0;
    	h.push(HeapNode(_s,0));
    	while (!flag[_t]&&!h.empty()){
    		int u=h.top().pos;
    		h.pop();
    		flag[u]=1;int nowd=mind[u];
    		if (u<=l) for (int i=g[u];i;i=e[i].next){
    			int v=e[i].adj;
    			if (mind[v]>nowd){
    				mind[v]=nowd;
    				h.push(HeapNode(v,mind[v]));
    			}
    		}
    		else if (u>cnt){
    			u=code[u-cnt];
    			for (int v=(pos[u]-1)%jump[u]+1;v<=l;v+=jump[u])
    				if (mind[v]>abs(pos[u]-v)/jump[u]+nowd){
    					mind[v]=abs(pos[u]-v)/jump[u]+nowd;
    					h.push(HeapNode(v,mind[v]));
    				}
    		}
    		else{
    			const int to[3]={(u-1)%l+1,u-(u-1)/l,u+(u-1)/l};
    			for (int i=0;i<3;++i){
    				int v=to[i];
    				if (i&&(u-1)/l!=(v-1)/l) continue;
    				if (mind[v]>nowd+(i>0)){
    					mind[v]=nowd+(i>0);
    					h.push(HeapNode(v,mind[v]));
    				}
    			}
    		}
    		
    		while (!h.empty()&&flag[h.top().pos]) h.pop();
    	}
    	if (!flag[_t]) mind[_t]=-1;
    }
    
    
    int main(){
    	l=read();doge=read();
    	n=cnt=l*(K+1);
    	memset(g,0,sizeof(g));m=1;
    	for (int i=1;i<=doge;++i){
    		pos[i]=read()+1;
    		if ((jump[i]=read())>K) code[(id[i]=++n)-cnt]=i;
    		else id[i]=jump[i]*l+pos[i];
    		AddEdge(pos[i],id[i]);
    	}
    	dijkstra(pos[1],pos[2]);
    	printf("%d
    ",mind[pos[2]]);
    	return 0;
    }
    

      

    T3

    题目大意:有n个人,每个人有2个房子,有一条河,每个房子都有可能在河任何一边的一个位置。要你建1或2座桥,使得每个人从一个房子到另一个房子(显然不能游泳)的最短距离之和最小,输出最小的和。n<=10^5

    当时考场上乱搞一通,连22分算法都想了好久(而且想出来了还无比复杂)……现在看来真是太傻比了。据说此题用各种乱搞的方法也能搞……

    首先,对于房子在河的一侧的,读入的时候就能处理掉。所以只需要处理在河2侧的就行了。

    对于建1座桥的数据,也就是22分,把所有数丢进去,求个中位数就完了

    对于建2座桥的数据,首先我们可以证明,把每个人按2个点的中点从左到右排序,最优解一定是一个前缀走左边的桥,一个后缀走右边的桥。至于具体证明,貌似随便画画图脑补脑补就行了?(虽然想不到TAT)

    接下来,问题就变成了,我们需要一个支持插入、删除、求中位数的数据结构,这样什么权值线段树、树状数组上二分、各种STL都能乱搞了

    这边顺便安利一下窝的【中根堆】,用4个priority_queue做的(如果不需要删除就只用2个),虽然不开O2很慢,但开了O2速度还是非常可观的。

    然后从左往右扫一遍就行了

    详见代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #define ll long long
    #define N 2000006
    
    using namespace std;
    inline int read(){
    	int ret=0;char ch=getchar();
    	while (ch<'0' || ch>'9') ch=getchar();
    	while ('0'<=ch && ch<='9'){
    		ret=ret*10-48+ch;
    		ch=getchar();
    	}
    	return ret;
    }
    
    int n,op;
    int a[N],cnt;
    int x[N],y[N];
    ll ans;
    
    struct MidHeap{
    	priority_queue<int> l,ld;
    	priority_queue<int,vector<int> ,greater<int> > r,rd;
    	int lsize,rsize;ll lsum,rsum;
    	void clear(){
    		lsize=rsize=lsum=rsum=0;
    		while (!l.empty()) l.pop();
    		while (!r.empty()) r.pop();
    		while (!ld.empty()) ld.pop();
    		while (!rd.empty()) rd.pop();
    	}
    	void refresh(){
    		while (l.size() && ld.size() && l.top()==ld.top()){
    			l.pop();ld.pop();
    		}
    		while (r.size() && rd.size() && r.top()==rd.top()){
    			r.pop();rd.pop();
    		}
    	}
    	void LtoR(){
    		int tmp=l.top();
    		--lsize;lsum-=(ll)tmp;l.pop();
    		++rsize;rsum+=(ll)tmp;r.push(tmp);
    	}
    	void RtoL(){
    		int tmp=r.top();
    		--rsize;rsum-=(ll)tmp;r.pop();
    		++lsize;lsum+=(ll)tmp;l.push(tmp);
    	}
    	void push(int value){
    		if (!lsize){
    			++lsize;lsum+=(ll)value;l.push(value);
    			return;
    		}
    		if (value<=l.top()){
    			++lsize;lsum+=(ll)value;l.push(value);
    			if (lsize>rsize+1) LtoR();
    			refresh();
    		}
    		else{
    			++rsize;rsum+=(ll)value;r.push(value);
    			if (rsize>lsize) RtoL();
    			refresh();
    		}
    	}
    	void erase(int value){
    		if (value<=l.top()){
    			--lsize;lsum-=(ll)value;ld.push(value);
    			refresh();
    			if (lsize<rsize) RtoL();
    			refresh();
    		}
    		else{
    			--rsize;rsum-=(ll)value;rd.push(value);
    			refresh();
    			if (rsize+1<lsize) LtoR();
    			refresh();
    		}
    	}
    	ll query(){
    		if (l.empty()) return 0;
    		ll mid=l.top();
    		return rsum-lsum+mid*(lsize-rsize);
    	}
    } h0,h1;
    
    inline bool cmp(const int &a,const int &b){
    	return x[a]+y[a]<x[b]+y[b];
    }
    
    int main(){
    	op=read();n=read();
    	cnt=0;ans=0;
    	if (op==1){
    		for (int i=1;i<=n;++i){
    			char c0=getchar();
    			while (!isalpha(c0)) c0=getchar();
    			int p0=read();
    			char c1=getchar();
    			while (!isalpha(c1)) c1=getchar();
    			int p1=read();
    			if (c0!=c1){
    				a[++cnt]=p0;
    				a[++cnt]=p1;
    				++ans;
    			}
    			else ans+=(ll)abs(p0-p1);
    		}
    		sort(a+1,a+cnt+1);
    		for (int i=1;i<=cnt;++i) ans+=(ll)abs(a[cnt/2]-a[i]);
    	}
    	else{
    		h0.clear();h1.clear();
    		for (int i=1;i<=n;++i){
    			char c0=getchar();
    			while (!isalpha(c0)) c0=getchar();
    			int p0=read();
    			char c1=getchar();
    			while (!isalpha(c1)) c1=getchar();
    			int p1=read();
    			if (c0!=c1){
    				h1.push(x[++cnt]=p0);
    				h1.push(y[cnt]=p1);
    				a[cnt]=cnt;
    				++ans;
    			}
    			else ans+=(ll)abs(p0-p1);
    		}
    		sort(a+1,a+cnt+1,cmp);
    		ll ans0=h1.query();
    		for (int i=1;i<cnt;++i){
    			h0.push(x[a[i]]);
    			h0.push(y[a[i]]);
    			h1.erase(x[a[i]]);
    			h1.erase(y[a[i]]);
    			ll now=h0.query()+h1.query();
    			if (now<ans0) ans0=now;
    		}
    		ans+=ans0;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    第二十九课 循环链表的实现
    第二十八课 再论智能指针(下)
    第二十七课 再论智能指针(上)
    第二十六课 典型问题分析(Bugfix)
    普通new和placement new的重载
    leetcode 581. Shortest Unsorted Continuous Subarray
    leetcode 605. Can Place Flowers
    leetcode 219. Contains Duplicate II
    leetcode 283. Move Zeroes
    leetcode 217. Contains Duplicate
  • 原文地址:https://www.cnblogs.com/wangyurzee7/p/5130825.html
Copyright © 2011-2022 走看看