zoukankan      html  css  js  c++  java
  • Codeforces 1131 (div 2)

    链接:http://codeforces.com/contest/1131

    A Sea Battle#

    利用良心出题人给出的图,不难看出答案为(2*(h1+h2)+2*max(w1,w2)+4)由于(w2 leq w1),所以答案为(2*(h1+h2)+2*w1+4)

    #include<cstdio>
    int w1,h1,h2,w2,ans;
    int main(){
    	scanf("%d%d%d%d",&w1,&h1,&w2,&h2);
    	ans+=(h1+h2)*2+w1*2+4;
    	printf("%d
    ",ans);
    }
    

    B Draw#

    这本质上可以说是一道贪心题吧,我们稍微分类讨论一下
    我们假设上一轮的结果为(x:y)显然可以分为3种情况,(x<y,x=y,x>y)
    (x<y)(x>y)本质一样我们只讨论一种
    (x<y)对于新一轮的比分(p:q)
    (y>p),那么说明在这几轮比赛中根本不可能出现,继续做就行,
    否则,我们可以让前一个人赢,赢球一时爽,一直赢球一直爽,赢到两个人比分一样,然后你拍一,我拍一就好了
    (x=y)的话,我们发现就是上面的子问题,直接你拍一,我拍一

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int ans=1,la,lb,a,b,n;
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++){
    		scanf("%d%d",&a,&b);
    		if (a==la&&b==lb)continue;
    		if (la>lb){
    			if (b<la){
    				la=a,lb=b;
    				continue;
    			}
    			ans+=min(a,b)-la+1;
    			la=a;lb=b;
    		}
    		else if (la<lb){
    			if (a<lb){
    				la=a,lb=b;
    				continue;
    			}
    			ans+=min(a,b)-lb+1;
    			la=a;lb=b;
    		}
    		else {
    			ans+=min(a,b)-la;
    			la=a,lb=b;
    		}
    	}
    	printf("%d
    ",ans);
    }
    

    C Birthday#

    这题啊,看起来有点难,其实只是纸老抚
    数据范围只有100,首先考虑乱搞
    发现乱搞凉了,怎么办?只会std::sort的选手突然想到,先排序!
    排完序了怎么办呢?我们考虑贪心地将排序后奇数位的数先按顺序填下去,然后将偶数位的数反过来填下去,这样就对了
    为什么呢?
    我们尝试证明一下这个贪心
    我们规定,我们填下去的数是从小到大填的,如果我们现在按这样填不是最优解,那么说明我们存在一种合法方案,使得让两个排完序后相邻的数在环中也相邻,并且这个方案更优
    我们设这位为第i位,则i和i+1在环中相邻
    然而我们意识到,如果这个方案要更优,则说明i与i+2是原来方案中差距最大的两个数,这样我们调整后才能得到一个更优的方案,一定不会变优
    我们开始考虑填入i+2,我们发现,i+2只能和i+1相邻,否则就会和一个小于等于i的数字相邻,那么这个新方案就一定不会比前面那个更优,了不起一样优嘛
    我们归纳一下,之后的每个数都存在这个问题,不能与小于等于i的数相邻,这样的环显然是不存在的,所以这个贪心是对的
    (不知道我在瞎bb啥,但是应该是对的,考场上我画了个图,现在懒得画了,大家意会一下吧略略略)

    #include<cstdio>
    #include<algorithm>
    int n,a[105],b[105],f[105];
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)scanf("%d",&a[i]);
    	std::sort(a+1,a+n+1);
    	b[1]=a[1];
    	int mid=(n>>1)+1;
    	b[(n>>1)+1]=a[n];f[1]=f[n]=1;
    	for (int i=2,j=3;i<mid;i++,j+=2)b[i]=a[j],f[j]=1;
    	for (int i=n,j=1;i>mid;j++)if (!f[j])b[i--]=a[j];
    	for (int i=1;i<=n;i++)printf("%d ",b[i]);
    } 
    

    D Gourmet choice#

    乍一看,差分约束!
    然后发现不会建图
    怎么办
    考虑乱搞
    我们先考虑假如给出的条件都是大于小于,那么要怎么做
    由NOIP2013 普及组 车站分级我们可以得到,拓扑排序!
    将所有点按拓扑序编号就行了
    如何处理等号?
    把用等号连接的点强行压成一个点
    当拓扑排序失败或出现自环则输出“No”,否则“Yes”,然后拓扑编号输出

    #include<cstdio>
    #include<vector>
    std::vector<int> v[2050];
    struct r{
    	int to,last;
    }e[4000050];
    int f[2050],dep[2050],num=1,mp[2050][2050],head[2050],h=1,q[2050],ans[2050],d[2050],t;
    char s[1050][1050];
    void add(int u,int v){e[num].to=v;e[num].last=head[u];head[u]=num++;}
    int get_fa(int u){return f[u]==u?u:f[u]=get_fa(f[u]);}
    int main(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n+m;i++)f[i]=i;
    	for (int i=1;i<=n;i++)scanf("%s",s[i]);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)if (s[i][j-1]=='='){
    			int fu=get_fa(i),fv=get_fa(j+n);
    			f[fv]=fu;
    		}
    	for (int i=1;i<=n+m;i++)v[get_fa(i)].push_back(i);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)if (s[i][j-1]=='<'){
    			int fu=get_fa(i),fv=get_fa(j+n);
    			if (fu==fv){
    				printf("No
    ");
    				return 0;
    			}
    			if (!mp[fu][fv])add(fu,fv),d[fv]++;
    		}
    		else if (s[i][j-1]=='>'){
    			int fu=get_fa(i),fv=get_fa(j+n);
    			if (fu==fv){
    				printf("No
    ");
    				return 0;
    			}
    			if (!mp[fu][fv])add(fv,fu),d[fu]++;
    		}
    	for (int i=1;i<=n+m;i++)if (f[i]==i&&!d[i])q[++t]=i,dep[i]=1;
    	while (h<=t){
    		int u=q[h++];
    		for (int i=head[u];i;i=e[i].last){
    			d[e[i].to]--;
    			if (!d[e[i].to]){
    				dep[e[i].to]=dep[u]+1;
    				q[++t]=e[i].to;
    			}
    		}
    	}
    	for (int i=1;i<=n+m;i++)if (f[i]==i&&!dep[i]){
    		printf("No
    ");
    		return 0;
    	}
    	printf("Yes
    ");
    	for (int i=1;i<=n+m;i++)
    		for (int j=v[i].size()-1;j>=0;j--)ans[v[i][j]]=dep[i];
    	for (int i=1;i<=n+m;i++){
    		printf("%d ",ans[i]);
    		if (i==n)putchar('
    ');
    	}
    }
    

    E String Multiplication#

    由于时间问题,蒟蒻博主并没有时间看完这道题的题面(由于当场F题过的人多所以先跳题了)
    考后发现,这题并不难
    我们读完题目可以发现,原来的串相邻的位置都被隔开了!利用这个就可以解决这题了
    我们可以发现,对于新串,对我们有用的信息只有最左有几个相同字母,最右有几个相同字母以及每种字母最多有多少个连续的
    于是我们可以分字母统计这个问题
    若新串首尾不同,则首字母的连续长度更新为max{新串最左的连续长度+1,串中该字母的最长连续长度}(最后一个字母也是一样的)
    如果相同(该串不止含有一种字母),则首尾字母的最长连续长度为max{新串最左的连续长度+1+最右连续长度,串中该字母的最长连续长度}
    若该串中只有一种字母,则这种字母连续长度更新为max{原串中连续长度*(新串长度+1),1}
    当然只要出现过的字母连续长度最少都为1,出现过的字母记得和1取max就好了
    按字母更新完后,在这些字母中找一个连续长度最大的输出长度就好了

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n,cnt[35],p[35],ans;
    char s[100050];
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++){
    		scanf("%s",s);
    		int len=strlen(s),tmp=1,ft,flag=0;
    		for (int j=0;j<26;j++)p[j]=(cnt[j]!=0);
    		for (int j=1;j<len;j++)if (s[j]==s[j-1])tmp++;
    		else{
    			if (!flag)ft=tmp,flag=1;
    			p[s[j-1]-'a']=max(p[s[j-1]-'a'],tmp);
    			tmp=1;
    		}
    		p[s[len-1]-'a']=max(p[s[len-1]-'a'],tmp);
    		if (!flag)p[s[0]-'a']=max(tmp+cnt[s[0]-'a']*tmp+cnt[s[0]-'a'],p[s[0]-'a']);
    		else if (s[0]==s[len-1])p[s[0]-'a']=max(cnt[s[0]-'a']?tmp+ft+1:0,p[s[0]-'a']);
    		else p[s[0]-'a']=max((cnt[s[0]-'a']!=0)+ft,p[s[0]-'a']),p[s[len-1]-'a']=max((cnt[s[len-1]-'a']!=0)+tmp,p[s[len-1]-'a']);
    		for (int j=0;j<26;j++)cnt[j]=p[j];
    	}
    	for (int i=0;i<26;i++)ans=max(cnt[i],ans);
    	printf("%d
    ",ans);
    } 
    

    F Asya And Kittens#

    蒟蒻博主因为数组开小,差点没过!心态爆炸
    这题呢,用心感受一下题目,每次把两个东西合并起来,是不是很像并查集
    没错,这题就是用的并查集
    首先我们把每个点初始化,合并的时候考虑新建一个点(tmp),把这次合并的两个点(fu,fv)都连到这个新点(tmp)上,并且(f[tmp]=f[fu]=f[fv]=tmp)
    然后(add\_edge(tmp,fu),add\_edge(tmp,fv))
    我们做完之后得到了什么呢,每次操作都增加了一层,一个点向两个点连边,这不就是棵树吗
    我们用(dfs)遍历这棵树,每次遇到(id leq n)的就加入答案就行了,由于这棵树形态非常优美,所以做出来就是对的

    #include<cstdio>
    struct r{
    	int to,last;
    }e[300050];
    int num=1,head[300050],ans[150050],cnt,tmp,n,f[300050],x,y;
    void add(int u,int v){
    	e[num].to=v;e[num].last=head[u];head[u]=num++;
    }
    int get_fa(int u){return f[u]==u?u:f[u]=get_fa(f[u]);}
    void dfs(int u){
    	if (u<=n)ans[++cnt]=u;
    	for (int i=head[u];i;i=e[i].last)dfs(e[i].to);
    }
    int main(){
    	scanf("%d",&n);
    	tmp=n;
    	for (int i=1;i<=n;i++)f[i]=i;
    	for (int i=1;i<n;i++){
    		scanf("%d%d",&x,&y);
    		int fu=get_fa(x),fv=get_fa(y);
    		if (fu!=fv){
    			tmp++;
    			add(tmp,fv),add(tmp,fu);
    			f[tmp]=f[fu]=f[fv]=tmp;
    		}
    	}
    	dfs(tmp);
    	for (int i=1;i<=n;i++)printf("%d ",ans[i]);
    }
    

    G Most Dangerous Shark#

    看了好几次题解都没看懂,我真是太弱啦
    最后买了一包山楂片,边吃,终于看懂了题解(这绝对不是帮山楂片打广告,正经博主怎么可能打广告呢)
    第一次做手动栈大题,题好神啊
    首先我们可以写出最原始的dp方程(dp[i]=min(dp[l[i]-1]+c[i],dp[j-1]+c[j](j<ileq r[j])))
    其中(l[i])表示将第(i)个骨牌向左推倒能推到的最左的骨牌,(r[i])当然就是向右推到最右的啦
    我们现在已经可以得到一个线段树解法了,但是本题需要我们在(O(n))的复杂度内解决这个问题
    我们可以尝试使用栈,我们先倒过来做,将元素压栈,若遇到(ileq s[top]+h[s[top]])那么我们就弹栈并且将弹出的元素的(l[i])设为(i+1)
    然后我们就得到了(l[i])
    而对于(r[i])的话,其实我们没必要把它求出来,只要边做边dp,然后维护一个栈中前缀最小值就好啦

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #define ll long long 
    std::vector <int> v1[250050],v2[250050];
    int k[250050],n,m,l[10000050],t,h[10000050],v,tmp,s[10000050];
    ll mn[10000050],c[10000050],dp[10000050];
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++){
    		scanf("%d",&k[i]);
    		for (int j=k[i];j;j--)scanf("%d",&v),v1[i].push_back(v);
    		for (int j=k[i];j;j--)scanf("%d",&v),v2[i].push_back(v); 
    	}
    	int q,id,mul;
    	scanf("%d",&q);
    	for (int i=1;i<=q;i++){
    		scanf("%d%d",&id,&mul);
    		for (int j=0;j<k[id];j++){
    			++tmp;h[tmp]=v1[id][j];
    			c[tmp]=v2[id][j]*1ll*mul;
    		}
    	}
    	for (int i=m;i>=1;i--){
    		while (t&&i<=s[t]-h[s[t]])l[s[t--]]=i+1;
    		s[++t]=i;
    	}
    	while (t)l[s[t--]]=1;mn[0]=1e18;
    	for (int i=1;i<=m;i++){
    		while (t&&i>=s[t]+h[s[t]])t--;
    		dp[i]=std::min(dp[l[i]-1]+c[i],mn[t]);
    		s[++t]=i;
    		mn[t]=std::min(mn[t-1],dp[i-1]+c[i]);
    	}
    	printf("%lld
    ",dp[m]);
    }
    
  • 相关阅读:
    VS Code 快捷键(中英文对照版)
    Linux下SVN提交时强制写日志
    如何搞定SVN目录的cleanup问题和lock问题
    Unity3D中利用Action实现自己的消息管理(订阅/发布)类
    Unity3D热更新之LuaFramework篇[03]--prefab加载和Button事件
    Unity3D热更新之LuaFramework篇[02]--用Lua创建自己的面板
    Unity3D热更新之LuaFramework篇[01]--从零开始
    Unity UI性能优化技巧
    Unity中雾效的开启
    解决Unity中模型部件的MeshCollider不随动画一起运动的问题
  • 原文地址:https://www.cnblogs.com/Cool-Angel/p/10440553.html
Copyright © 2011-2022 走看看