• 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;
    }
    

      

  • 相关阅读:
    pycharm 安装第三方库,出现错误: error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visual studio.com/visual-cpp-build-tools
    c# 开发常用小方法
    [LeetCode]28. 实现 strStr()
    [LeetCode]27. 移除元素
    [LeetCode]21. 合并两个有序链表
    [LeetCode]20. 有效的括号
    [LeetCode]14. 最长公共前缀
    [LeetCode]13. 罗马数字转整数
    [LeetCode]9. 回文数
    [LeetCode]2. 两数相加
  • 原文地址:https://www.cnblogs.com/wangyurzee7/p/5130825.html
Copyright © 2020-2023  润新知