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 lesson18homework

    package com.xt.lesson19; import java.util.Scanner;import java.util.Set;import java.util.TreeSet; /** ...

  2. 离线下载pytorch安装包

    1. 选择合适的安装包下载 https://anaconda.org/pytorch/repo?type=conda&label=main 2. 安装命令: conda install 安装包 ...

  3. sql注入搞事情(连载二)

    sql注入(连载二)安信华web弱点测试系统注入 好多人问我sql怎么学习,我一下也说不出来.我就在此做统一的解答: sql语句分为两种,不管怎么用还是怎么学习主要是要理解SQL语句的基本概念,框架, ...

  4. XML和XML解析

    1. XML文件: 什么是XML?XML一般是指可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言. 2.XML文件的优点: 1)XML文档内容和结构完全分离. 2 ...

  5. PHP的八种数据类型

    PHP 支持8种基本的数据类型. 四种标量类型: boolean (布尔型) integer (整型) float (浮点型, 也称作 double) string (字符串) 两种复合类型: arr ...

  6. 【学】第一节 rt-thread的rt_kprintf功能的实现

    @2019-06-20 [主题] 拿到一个stm32f407新板,使用rt-thread源码中的bsp中的stm32f407-discovery工程,操作串口外设实现rt_kprintf功能 [问题] ...

  7. QTP(13)

    练习1:Flight4a 要求: a.录制Flight4a登录+购票+退出业务流程 b.实现登录1次,购票3次,退出1次 c.对Fly From.Fly to.航班实现随机参数化 随机参数化:Rand ...

  8. zencart新增categories分类表字段步骤

    zencart新增分类字段步骤 1.categories表新增字段related_categories.related_products ) ) NOT NULL; 2.修改admin\categor ...

  9. java线程基础巩固---Thread中断Interrupt方法学习&采用优雅的方式结束线程生命周期

    Interrupt学习: 在jdk中关于interrupt相关方法有三个,如下: 关于上面的疑问会在稍后进行阐述滴,下面看代码: 编译运行: 应该说是t线程为啥在被打断之后没有退出,还是在运行状态,这 ...

  10. Codeforces 1082 毛毛虫图构造&最大权闭合子图

    A #include<bits/stdc++.h> using namespace std; typedef long long ll; , MAXM = ; //int to[MAXM ...