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 部分题解的更多相关文章

  1. 2016 Al-Baath University Training Camp Contest-1

    2016 Al-Baath University Training Camp Contest-1 A题:http://codeforces.com/gym/101028/problem/A 题意:比赛 ...

  2. 2016 Multi-University Training Contest 3 部分题解

    1001,只要枚举区间即可.签到题,要注意的是输入0的话也是“TAT”.不过今天补题的时候却WA了好几次,觉得奇怪.原来出现在判断条件那里,x是一个int64类型的变量,在进行(x<65536* ...

  3. 2016 Multi-University Training Contest 1 部分题解

    第一场多校,出了一题,,没有挂零还算欣慰. 1001,求最小生成树和,确定了最小生成树后任意两点间的距离的最小数学期望.当时就有点矛盾,为什么是求最小的数学期望以及为什么题目给了每条边都不相等的条件. ...

  4. 2016 Multi-University Training Contest 2 部分题解

    1009,直接贪心,只要让后面的尽量小,第一位和第二位尽量大即可. 1011,直接统计奇数的字母的个数,然后用偶数的个数平均分配到它们上面即可.代码如下: #include <stdio.h&g ...

  5. 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 ...

  6. 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 ...

  7. 2016 Al-Baath University Training Camp Contest-1 A

    Description Tourist likes competitive programming and he has his own Codeforces account. He particip ...

  8. 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 ...

  9. 2018 Multi-University Training Contest 3(部分题解)

    Problem F. Grab The Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Ja ...

随机推荐

  1. 进阶Java编程(8)反射应用案例

    1,反射实例化对象 经过一系列分析之后可以发现虽然获得了Class类的实例化对象但是依然觉得这个对象获取的意义不是很大,所以为了进一步的帮助大家理解反射的核心意义所在,下面将通过几个案例进行说明(都是 ...

  2. 【原创】大叔经验分享(68)maven工程查看jar包依赖

    1 idea 结果 2 maven命令 $ mvn dependency:tree 结果 [INFO] +- org.springframework.boot:spring-boot-starter- ...

  3. O061、Boot from Volume

    参考https://www.cnblogs.com/CloudMan6/p/5679384.html   Volume 除了可以用作Instance的数据盘,也可以作为启动盘(Bootable Vol ...

  4. React学习——通过模态框中的表单,学习父子组件之间传值

    import { Button, Modal, Form, Input, Radio } from 'antd'; const CollectionCreateForm = Form.create({ ...

  5. lumen时区

    今天用 Lumen 框架写代码时, 也是初次体验 Lumen, 遇到了一个问题, 从数据库里查出的时间比数据库里保存的 TIMESTAMP 时间慢了8个小时, 很明显这是一个时区设置的问题, 本以为可 ...

  6. 实现代码重启android app.

    var Form1: TForm1; implementation uses System.DateUtils, Androidapi.JNI.GraphicsContentViewText, FMX ...

  7. Shell脚本——for,while,until循环

    1.for循环: 语句格式: for i in 循环判断 do 循环体 done 举例:九九乘法表(for循环版本) #!/bin/bash # Author: Sean Martin # Blog: ...

  8. Mysql(三)-3:完整性约束

    一 介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性主要分为: PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 FOREIGN KEY ...

  9. mybatis 动态sql 的笔记 以及标签

    MyBatis常用OGNL表达式 e1 or e2 e1 and e2 e1 == e2,e1 eq e2 e1 != e2,e1 neq e2 e1 lt e2:小于 e1 lte e2:小于等于, ...

  10. 裸磁盘上ext4与xfs在线扩容,非LVM

    虚拟机添加一个20G的硬盘,磁盘为sdb,分区为ext4 格式化一个5Gib的磁盘出来,用dd命令写入4G数据. 一.需求是容量为5G的磁盘,文件系统为ext4的sdb1扩容到10G. 操作步骤为 1 ...