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. MyBatis-plus 新增实例,主键ID从很大的数字开始

    mybatis-plus是mybatis增强版,用mybatis-plus可以省去很多DAO层代码和数据库操作语句的编写.但是需要我们配置好条件. 情景: 向数据库新增一条实例,我们要调用 inser ...

  2. git 多账户添加ssh秘钥

    生成秘钥的步骤: ssh-keygen -t rsa -C "xxxx@qq.com" 添加秘钥 在不同的域中添加相同的秘钥是没有问题的,比如 github.com / code. ...

  3. (十一)设置关闭多核cpu的核

    echo 0 > /sys/devices/system/cpu/cpu3/online 查看当前有哪些核心 cat  /sys/devices/system/cpu/online

  4. DBUtils封装数据库返回对象的各种方法

    ①ArrayHandler:     将查询结果的第一行数据,保存到Object数组中       ②ArrayListHandler     将查询的结果,每一行先封装到Object数组中,然后将数 ...

  5. 1.Shell脚本

    1.Shell脚本 可以将Shell终端解释器当作人与计算机硬件之间的“翻译官”,它作为用户与Linux系统内部的通信媒介,除了能够支持各种变量与参数外,还提供了诸如循环.分支等高级编 程语言才有的控 ...

  6. Python——print函数输出对齐问题

    原创声明:本文系博主原创文章,转载及引用请注明出处. 当我们使用print函数时,若指定输出宽度,例如: >>> import math >>> print('|P ...

  7. Tampermonkey油猴脚本管理插件-最强浏览器插件的安装使用全攻略

      对于接触过谷歌浏览器插件的“玩家”们来说,应该没有人没听说过Tampermonkey用户脚本管理器,也就是中文所说的“油猴”这个chrome插件了. 油猴号称全商店最强的浏览器插件绝非浪得虚名,一 ...

  8. error LNK2019 : unresolved external symbol Zbar配置问题

    原文链接:https://blog.csdn.net/MengchiCMC/article/details/77871714 出现error LNK2019 : unresolved external ...

  9. solr 查询解析器

    定义 查询解析器用于将查询语句(q参数)解析成搜索语法. 默认解析器:lucene Solr在查询的时候,用到了QueryParser对用户输入做解析,solr默认使用的解析器是lucene,被称之为 ...

  10. 使用python获得屏幕截图并保存为位图文件

    直接上代码: import win32gui import win32ui from ctypes import windll import Image hwnd = win32gui.FindWin ...