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 ...
随机推荐
- Winform 5种皮肤小结(内含丰富的下载实例)
软件界面就是指软件中面向操作者而专门设计的用于操作使用及反馈信息的指令部分. 优秀的软件界面有简便易用,突出重点,容错高等特点. 1.东日IrisSkin 使用IrisSkin只能是对单一的控件重绘 ...
- HTML的学习2(注释)
<!--链接标签--> <!--核心属性就是href 属性值可以是一个跳转的地址--> <a href="">可点击的文本</a> ...
- 阿里巴巴开源框架java诊断工具--Arthas
下载:arthas wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar --target-i ...
- ubuntu更改apt源为阿里云源
https://blog.csdn.net/zhangjiahao14/article/details/80554616 https://yq.aliyun.com/articles/704603 h ...
- 1.Shell脚本
1.Shell脚本 可以将Shell终端解释器当作人与计算机硬件之间的“翻译官”,它作为用户与Linux系统内部的通信媒介,除了能够支持各种变量与参数外,还提供了诸如循环.分支等高级编 程语言才有的控 ...
- python3.6 错误: ModuleNotFoundError:No module named "Crypto"
原因及处理:在使用python是经常会用到import一个第三方库,但是有时候会提示某个模块不存在,如Crypto其实是因为Python3里面这个模块的名字变了, pip install pycryp ...
- CNN for NLP(2)
参考链接: 卷积神经网络(CNN)在句子建模上的应用, 卷积神经网络CNN在自然语言处理中的应用, CNN在NLP中的应用.
- zencart简易页面ezpage后台编辑位置
zencart简易页面ezpage后台编辑位置: 后台-Tools(工具)-EZ-Pages(简易页面管理) 若编辑之后无法保存,说明includes\languages\语言包\html_inclu ...
- LeetCode 01 两数之和
链接:https://leetcode-cn.com/problems/two-sum 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们 ...
- MyBatis-01-简介
基础知识: JDBC Mysql Java基础 Maven Junit 框架:是有配置文件的.最好的方式:看官网文档 1.简介 1.1.什么是MyBatis 简介 什么是 MyBatis? MyBat ...