2016 Multi-University Training Contest 4 部分题解
1001,官方题解是直接dp,首先dp[i]表示到i位置的种类数,它首先应该等于dp[i-1],(假设m是B串的长度)同时,如果(i-m+1)这个位置开始到i这个位置的这一串是和B串相同的,那么dp[i]还应该加上dp[i-m],因为从i-m+1开始可以被替换成另外一种意思。详细的见代码吧。我们当时使用dfs来做的,实际上换汤不换药,思想是一样的(不过dfs的话是从前往后算的)。代码如下:
- #include <bits/stdc++.h>
- using namespace std;
- const int N = + ;
- const int mod = (int)1e9 + ;
- char a[N],b[N];
- int nxt[N],n,m,dp[N];
- bool isok[N];
- void getnxt()
- {
- memset(nxt,,sizeof(nxt));
- nxt[] = ;
- int j = ;
- for(int i=;b[i];i++)
- {
- while(j> && b[j+]!=b[i]) j = nxt[j];
- if(b[j+] == b[i]) j++;
- nxt[i] = j;
- }
- }
- void kmp()
- {
- memset(isok,false,sizeof(isok));
- int j = ;
- for(int i=;a[i];i++)
- {
- while(j> && b[j+]!=a[i]) j=nxt[j];
- if(b[j+]==a[i]) j++;
- if(j==m)
- {
- isok[i-m+] = true;
- j = nxt[j];
- }
- }
- }
- int dfs(int x)
- {
- if(x==n+) return ;
- if(dp[x] != -) return dp[x];
- if(!isok[x]) return dp[x] = dfs(x+)%mod;
- else
- {
- int ret = ;
- ret = dfs(x+) % mod;
- ret += dfs(x+m) % mod;
- return dp[x] = ret%mod;
- }
- }
- int main()
- {
- int T;scanf("%d",&T);
- for(int kase=;kase<=T;kase++)
- {
- scanf("%s%s",a+,b+);
- m=strlen(b+);n=strlen(a+);getnxt();kmp();
- /*memset(dp,0,sizeof(dp));
- dp[0] = 1;
- for(int i=1;i<=n;i++)
- {
- dp[i] = dp[i-1];
- if(i-m+1>=1)
- {
- if(isok[i-m+1]) dp[i] += dp[i-m];
- }
- dp[i] %= mod;
- }
- printf("Case #%d: %d\n",kase,dp[n]);*/
- // 上面是题解的方法
- // 下面是队友的方法
- memset(dp,-,sizeof(dp));
- int ans = dfs() % mod;
- printf("Case #%d: %d\n",kase,ans);
- }
- }
1010,LIS,题解的方法很不错,数列中的每个数都减去这个数字之前的0的个数再做LIS,然后最后最大的LIS数加上整个串内0的个数即可。代码如下:
- #include <bits/stdc++.h>
- using namespace std;
- const int N = + ;
- const int inf = 0x3f3f3f3f;
- int a[N],dp[N];
- int main()
- {
- int T;scanf("%d",&T);
- for(int kase=;kase<=T;kase++)
- {
- int n;scanf("%d",&n);
- for(int i=;i<=n;i++) scanf("%d",a+i);
- memset(dp,inf,sizeof(dp));
- int cnt = ;
- for(int i=;i<=n;i++)
- {
- if(a[i])
- {
- a[i] -= cnt;
- *lower_bound(dp+,dp++n,a[i]) = a[i];
- }
- else cnt++;
- }
- int ans = lower_bound(dp+,dp++n,inf) - (dp+) + cnt;
- printf("Case #%d: %d\n",kase,ans);
- }
- }
另外,队友当时是用dp加二分的方法做的,但是我看不懂0.0,代码先放下,以后再看吧= =:
- #include<bits/stdc++.h>
- using namespace std;
- const int N = 1e5+;
- int num[N];
- int dp[N];
- int erfen(int l,int r,int x)
- {
- if(r == l) return r;
- if(x > dp[r]) return r;
- while(r - l > )
- {
- int mid = (r+l) /;
- if(dp[mid] <= x) l = mid;
- else r = mid;
- }
- return r;
- }
- int main()
- {
- int T;
- cin >> T;
- for(int cnt = ;cnt <= T;cnt ++)
- {
- int n;
- scanf("%d",&n);
- for(int i= ;i <= n;i ++)
- {
- scanf("%d",&num[i]);
- }
- memset(dp,0x3f,sizeof(dp));
- int top = ;
- for(int i = ;i <= n;i ++)
- {
- if(num[i]!=)
- {
- int now = erfen(,top,num[i]);
- if(num[i] == dp[now-]) continue;
- dp[now] = num[i];
- if(now == top) top ++;
- }
- else
- {
- int now = erfen(,top,num[i]);
- //cout << i << ' ' << now << ' ' << top << endl;
- for(int j = top-;j >= now ;j --)
- {
- if(dp[j] + < dp[j+]) dp[j+] = dp[j]+;
- }
- top ++;
- if(now == ||dp[now-] == ) {dp[now] = ;}
- }
- /*for(int i= 1;i < top;i ++) cout << i << ' ' << dp[i] << endl;
- cout <<"##########" << i << ' ' <<top << endl;*/
- }
- printf("Case #%d: %d\n",cnt,top-);
- }
- return ;
- }
1011,是个大水题,反正我当时题目也没看= =。现在看了一下队友的代码,getline的方法还是值得一学的。队友代码如下:
- #include<bits/stdc++.h>
- using namespace std;
- string s[] = {
- "Cleveland Cavaliers",
- "Golden State Warriors",
- "San Antonio Spurs",
- "Miami Heat",
- "Miami Heat",
- "Dallas Mavericks",
- "L.A. Lakers",
- "L.A. Lakers",
- "Boston Celtics",
- "San Antonio Spurs",
- "Miami Heat",
- "San Antonio Spurs",
- "Detroit Pistons",
- "San Antonio Spurs",
- "L.A. Lakers",
- "L.A. Lakers",
- "L.A. Lakers",
- "San Antonio Spurs",
- "Chicago Bulls",
- "Chicago Bulls",
- "Chicago Bulls",
- "Houston Rockets",
- "Houston Rockets",
- "Chicago Bulls",
- "Chicago Bulls",
- "Chicago Bulls",
- "Detroit Pistons",
- "Detroit Pistons",
- "L.A. Lakers",
- "L.A. Lakers",
- "Boston Celtics",
- "L.A. Lakers",
- "Boston Celtics",
- "Philadelphia 76ers",
- "L.A. Lakers",
- "Boston Celtics",
- "L.A. Lakers",
- "Seattle Sonics",
- "Washington Bullets",
- "Portland Trail Blazers",
- "Boston Celtics",
- "Golden State Warriors",
- "Boston Celtics",
- "New York Knicks",
- "L.A. Lakers",
- "Milwaukee Bucks",
- "New York Knicks",
- "Boston Celtics",
- "Boston Celtics",
- "Philadelphia 76ers",
- "Boston Celtics",
- "Boston Celtics",
- "Boston Celtics",
- "Boston Celtics",
- "Boston Celtics",
- "Boston Celtics",
- "Boston Celtics",
- "Boston Celtics",
- "St. Louis Hawks",
- "Boston Celtics",
- "Philadelphia Warriors",
- "Syracuse Nats",
- "Minneapolis Lakers",
- "Minneapolis Lakers",
- "Minneapolis Lakers",
- "Rochester Royals",
- "Minneapolis Lakers",
- "Minneapolis Lakers",
- "Baltimore Bullets",
- "Philadelphia Warriors",
- };
- int main(){
- int T,kase = ;
- cin >> T;
- getchar();
- while(T --){
- int cnt = ;
- string str;
- getline(cin,str);
- for(int i = ; i < ; i ++){
- if(str == s[i]) cnt ++;
- }
- printf("Case #%d: %d\n",kase ++,cnt);
- }
- return ;
- }
1012,我们一开始以为是个技巧题(只要想到了方法就能过),因为看我们学校其他队都过了这题然而我们却卡了很久,我们以为是很简单的- -结果就没想着用线段树去写。。没想到题解真的是用树状数组维护的- -!这个题目的关键是怎么找出一个数字右边有多少个比它小的数字,并且不能用n^2来实现。一开始用的set实现,结果set根本没有迭代器相减的功能!于是就“顺理成章”的卡了很久。。最后终于是用线段树成段更新写出来了。我的思路大致是这样的,找一个数右边有多少个比它小的数,那么我从左边开始扫描,对一个数来讲,比方说5,那么它右边最多只有4个数字比它小,但是如果5的左边已经出现了一个比它小的数,那么5右边比它小的数字的个数将减少1,那么我们怎么使用线段树呢?比方说左边过来是4,5,6,先扫描到4,那么比4大的区间(即[5,n])的懒惰标记都加1,表示的是比它大的数字的右边的比它们小的数的个数减少了1(例如这里,5和6因为4比它们小,所以右边的比它们小的数的个数显然都减少了一个),那么我再扫描到5的时候,5右边比它小的数的个数就是(5-1)-add[5]=4-1=3了。
那么顺便再讲下这题最后的思路,我们当时讨论的是某个数的答案应该等于(这个数字i,它的位置pos[i],pos[i]+d[i])这三个数字中,两两差值最大的一个(其中d[i]表示的是i这个数字右边比它小的数的个数)。pos[i]+d[i]是什么意思呢?因为“考虑一个位置上的数字c在冒泡排序过程的变化情况。c会被其后面比c小的数字各交换一次,之后c就会只向前移动”,所以pos[i]+d[i]就是其移动的一个右边的位置(事实上是最右边的位置,为什么是最右边后面再讲)。这样代码就可以写出来了,现场的代码如下:
- #include<cstdio>
- #include<bits/stdc++.h>
- #define t_mid (l+r >> 1)
- #define ls (o<<1)
- #define rs (o<<1 | 1)
- #define lson ls,l,t_mid
- #define rson rs,t_mid+1,r
- using namespace std;
- const int N = 1e5+;
- int num[N];
- int d[N];
- int pos[N];
- int c[N<<],add[N<<];
- void up(int o) {c[o] = c[ls] + c[rs];}
- void down(int o,int len)
- {
- if(add[o])
- {
- add[ls] += add[o];
- add[rs] += add[o];
- c[ls] += add[o] * (len - (len >> ) );
- c[rs] += add[o] * (len >> );
- add[o]=;
- }
- }
- int build(int o,int l,int r)
- {
- if(l==r) return c[o]=;
- return c[o] = build(lson) + build(rson);
- }
- void update(int o,int l,int r,int ql,int qr,int dt)
- {
- //printf("%d %d %d %d %d !!\n",o,l,r,ql,qr);
- if(ql == l && qr == r)
- {
- add[o] += dt;
- c[o] += dt * (r-l+);
- return;
- }
- down(o,r-l+);
- if(qr <= t_mid) update(lson,ql,qr,dt);
- else if(ql>t_mid) update(rson,ql,qr,dt);
- else
- {
- update(lson,ql,t_mid,dt);
- update(rson,t_mid+,qr,dt);
- }
- up(o);
- }
- int query(int o,int l,int r,int ql,int qr)
- {
- if(ql == l && qr == r) return c[o];
- down(o,r-l+);
- int res = ;
- if(qr <= t_mid) res+=query(lson,ql,qr);
- else if(ql>t_mid) res+=query(rson,ql,qr);
- else
- {
- res+=query(lson,ql,t_mid);
- res+=query(rson,t_mid+,qr);
- }
- return res;
- }
- void init()
- {
- memset(add,,sizeof(add));
- }
- int main()
- {
- int T;
- scanf("%d",&T);
- int kase = ;
- while(T--)
- {
- int n;
- scanf("%d",&n);
- for(int i= ;i <= n;i ++)
- {scanf("%d",&num[i]);pos[num[i]]=i;}
- memset(d,,sizeof(d));
- //d[n] = 0;
- build(,,n);
- init();
- for(int i=;i<=n;i++)
- {
- int t = num[i];
- //printf("%d !\n",t);
- if(t<n) update(,,n,t+,n,);
- d[t] = t- - (query(,,n,t,t));
- }
- //printf("!!#### %d \n",d[5]);
- printf("Case #%d: ",kase++);
- for(int i=;i<=n;i++)
- {
- //printf("%d%c",max(abs(pos[i]-i),d[i]),i==n?'\n':' ');
- int aa = i,bb=pos[i],cc= pos[i]+d[i];
- int aaa=abs(aa-bb),bbb=abs(aa-cc),ccc=abs(bb-cc);
- printf("%d%c",max(aaa,max(bbb,ccc)),i==n?'\n':' ');
- }
- }
- }
接着我们继续讨论上面的问题,显然的左边的位置应该是min(i,pos[i]),那么pos[i]+d[i]和 i 是谁比较大一点呢?如果pos[i]>=i,那么肯定前者大,现在我们考虑pos[i]<i的情况,也就是说 i 这个数一开始被放在了它正确的位置的左边,我们为了让前者更小,就让d[i]更小,那么 i 这个数的右边应该尽量使比它大的数,但是即便是这样也只能在 i 正确的位置之后才有可能填充满比它大的数,如果这样那么至少从pos[i]+1一直到 i 这么多的位置上都是比 i 小的数,换言之,d[i]>=i-pos[i],移项得pos[i]+d[i]>=i,即前者大,举个例子好了,比方说7我把它放在了7号位置以前,假设比它大的都放在后面了,这样d[7]会更小一点,那么假设7放在了5号位,那么6,7都是比7小的数字,放在了x号位,那么x+1号位一直到7号位都是比7小的数字,即d[7]>=7-x=7-pos[7]。很显然就是上面的结论了。
所以说最左边的位置是min(i,pos[i]),最右边的位置是pos[i]+d[i]。
这样的话代码可是得到一点优化,修改后的代码如下:
- #include<cstdio>
- #include<bits/stdc++.h>
- #define t_mid (l+r >> 1)
- #define ls (o<<1)
- #define rs (o<<1 | 1)
- #define lson ls,l,t_mid
- #define rson rs,t_mid+1,r
- using namespace std;
- const int N = 1e5+;
- int num[N];
- int d[N];
- int pos[N];
- int c[N<<],add[N<<];
- void up(int o) {c[o] = c[ls] + c[rs];}
- void down(int o,int len)
- {
- if(add[o])
- {
- add[ls] += add[o];
- add[rs] += add[o];
- c[ls] += add[o] * (len - (len >> ) );
- c[rs] += add[o] * (len >> );
- add[o]=;
- }
- }
- int build(int o,int l,int r)
- {
- if(l==r) return c[o]=;
- return c[o] = build(lson) + build(rson);
- }
- void update(int o,int l,int r,int ql,int qr,int dt)
- {
- if(ql == l && qr == r)
- {
- add[o] += dt;
- c[o] += dt * (r-l+);
- return;
- }
- down(o,r-l+);
- if(qr <= t_mid) update(lson,ql,qr,dt);
- else if(ql>t_mid) update(rson,ql,qr,dt);
- else
- {
- update(lson,ql,t_mid,dt);
- update(rson,t_mid+,qr,dt);
- }
- up(o);
- }
- int query(int o,int l,int r,int ql,int qr)
- {
- if(ql == l && qr == r) return c[o];
- down(o,r-l+);
- int res = ;
- if(qr <= t_mid) res+=query(lson,ql,qr);
- else if(ql>t_mid) res+=query(rson,ql,qr);
- else
- {
- res+=query(lson,ql,t_mid);
- res+=query(rson,t_mid+,qr);
- }
- return res;
- }
- void init()
- {
- memset(add,,sizeof(add));
- }
- int main()
- {
- int T;
- scanf("%d",&T);
- int kase = ;
- while(T--)
- {
- int n;
- scanf("%d",&n);
- for(int i= ;i <= n;i ++)
- {scanf("%d",&num[i]);pos[num[i]]=i;}
- memset(d,,sizeof(d));
- build(,,n);
- init();
- for(int i=;i<=n;i++)
- {
- int t = num[i];
- if(t<n) update(,,n,t+,n,);
- d[t] = t- - (query(,,n,t,t));
- }
- printf("Case #%d: ",kase++);
- for(int i=;i<=n;i++)
- {
- int left = min(i,pos[i]),right = pos[i]+d[i];
- printf("%d%c",right - left,i==n?'\n':' ');
- }
- }
- }
理解透了的话,这道题还是相当有意思的~
2016 Multi-University Training Contest 4 部分题解的更多相关文章
- 2016 Al-Baath University Training Camp Contest-1
2016 Al-Baath University Training Camp Contest-1 A题:http://codeforces.com/gym/101028/problem/A 题意:比赛 ...
- 2016 Multi-University Training Contest 3 部分题解
1001,只要枚举区间即可.签到题,要注意的是输入0的话也是“TAT”.不过今天补题的时候却WA了好几次,觉得奇怪.原来出现在判断条件那里,x是一个int64类型的变量,在进行(x<65536* ...
- 2016 Multi-University Training Contest 1 部分题解
第一场多校,出了一题,,没有挂零还算欣慰. 1001,求最小生成树和,确定了最小生成树后任意两点间的距离的最小数学期望.当时就有点矛盾,为什么是求最小的数学期望以及为什么题目给了每条边都不相等的条件. ...
- 2016 Multi-University Training Contest 2 部分题解
1009,直接贪心,只要让后面的尽量小,第一位和第二位尽量大即可. 1011,直接统计奇数的字母的个数,然后用偶数的个数平均分配到它们上面即可.代码如下: #include <stdio.h&g ...
- 2016 Al-Baath University Training Camp Contest-1 E
Description ACM-SCPC-2017 is approaching every university is trying to do its best in order to be th ...
- 2016 Al-Baath University Training Camp Contest-1 F
Description Zaid has two words, a of length between 4 and 1000 and b of length 4 exactly. The word a ...
- 2016 Al-Baath University Training Camp Contest-1 A
Description Tourist likes competitive programming and he has his own Codeforces account. He particip ...
- 2016 Al-Baath University Training Camp Contest-1 I. March Rain —— 二分
题目链接:http://codeforces.com/problemset/gymProblem/101028/I I. March Rain time limit per test 2 second ...
- 2018 Multi-University Training Contest 3(部分题解)
Problem F. Grab The Tree Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 524288/524288 K (Ja ...
随机推荐
- spring-cloud 学习二 服务发现
注册中心服务发现的例子 添加module pom文件如下 <?xml version="1.0" encoding="UTF-8"?> <pr ...
- EF 查询扩展
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Da ...
- Java Jersey的详情概述
Jersey是一个RESTFUL请求服务JAVA框架,与常规的JAVA编程使用的struts框架类似,它主要用于处理业务逻辑层. 与springmvc 的区别: 1. jersey同样提供DI,是由g ...
- 正着打星星(js)
//让用户输入行数,使用for循环嵌套打出正着的星星来,行数等于用户输入的数字 //例如:用户输入6 // * // *** // ***** // ******* // ********* // * ...
- Matplotlib 随机漫步图
import matplotlib.pyplot as plt from random import choice class Randomwalk(): def __init__(self,num_ ...
- ORA-01145: offline immediate disallowed unless media recovery enabled问题解决
ORA-01145: offline immediate disallowed unless media recovery enabled (随记,后续整理) 数据库只有在归档模式下才能够直接对数据文 ...
- -sh: ./a.out: not found
感觉程序没有问题,编译生成a.out后,拷贝到开发板执行,提示 -sh: ./a.out: not found. 上网查找问题,大概原因有2个 一就是有可能/lib下面没有C库 就是没有glibc或者 ...
- 03-【request对象获取请求的数据 & request对象存取值】
request概述(封装了客户端所有的请求数据) request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest.在 ...
- deep_learning_Function_sklearn的train_test_split()
sklearn的train_test_split train_test_split函数用于将矩阵随机划分为训练子集和测试子集,并返回划分好的训练集测试集样本和训练集测试集标签. 格式: X_tra ...
- ProgressDialog 进度条的初步认识
public class MainActivity extends Activity implements View.OnClickListener{ private ProgressBar prog ...