sc测试二

来源:互联网 发布:网络服务器托管 编辑:程序博客网 时间:2024/06/06 20:49

在这组测试题中罕见地发现了水题,先从这题开始说起。

打印机

题目描述

打印机

有一台打印机,它能打出一张像素为n×m的图片(可以把图片想象为一个nm列的表格)。一开始所有像素点都是0号颜色。我们将对图片依次进行k次绘制操作,有两种操作:

把第r行的所有像素都绘制成c号颜色。

把第c列的所有像素都绘制成c号颜色。

你需要计算打印机打印出来的图片的每个像素点的颜色。

输入格式

第一行三个整数n, m, k n, m, k ≥ 1

接下来k行,每行三个整数x, i, c表示一个操作(c ≥ 1):

如果x=1,那么表示把第i行的所有像素都绘制成c号颜色。

如果x=2,那么表示把第i列的所有像素都绘制成c号颜色。

输出格式

n行,每行m个表示颜色编号的整数。

输入样例

3 3 3
1 1 3
2 2 1
1 2 2

输出样例

3 1 3
2 2 2
0 1 0

    这题现在的我会做,相信未来的我也会做。把每一行与每一列涂色的时间记录下来。我想过如果有一行重复染色怎么办,事实证明后染者会覆盖掉。

代码如下:

#include#includeusing namespace std;int x,f,c,n,m,k;struct ee{int num,time;}h[100005],z[100005];int main(){freopen("2100.in","r",stdin);freopen("2100.out","w",stdout);cin>>n>>m>>k;for(int i=1;i<=k;i++){cin>>x>>f>>c;if(x==1) {h[f].num=c;h[f].time=i;}else {z[f].num=c;z[f].time=i;}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(h[i].num!=0&&z[j].num!=0){if(h[i].time

假期/放假

题目描述

经过几个月辛勤的工作,FJ决定让奶牛放假。假期可以在1…N天内任意选择一段(需要连续),每一天都有一个享受指数W。但是奶牛的要求非常苛刻,假期不能短于P天,否则奶牛不能得到足够的休息;假期也不能超过Q天,否则奶牛会玩的腻烦。FJ想通过安排一次假期,奶牛们获得的享受指数(假期内每天的享受指数之和)最大。

50% 1≤N≤10000
100% 1≤N≤100000
<=P<=Q<=n

输入格式 2136.in

第一行:N,P,Q.
第二行:N个数字,中间用一个空格隔开。

输出格式 2136.out

一个整数,奶牛们能获得的最大享受指数。

输入样例 2136.in

5 2 4
-9 -4 -3 8 -6
 

输出样例 2136.out

5

(选择第3-4天,享受指数为-3+8=5

当时的情况:

    把所有题目浏览了一遍以后,发现才半小时,有很多同学都AC了这题,以为是超级大水题。用简单的思路研究了半天,结果用简单的加加减减,没有什么用,终究逃不出n^2。

正解:

    部分和+单调队列。题目棘手的地方主要是规定了段的长度,并且这个长度不是一个定值,并且这个长度在一定的范围以内。

    这样就起到单调队列的作用了,当对头的坐标超过这一段的距离便把它踢出去。算出每一段的部分和。枚举到,如果当前这一段的部分和比单调队列队尾的小,便存进去,保持一个单调下降序列。最后用枚举到的i,减去队列头(既部分和最小的某段),就是正确答案。

    为什么要这么做呢?因为用当前的部分和sum[i]减去最小的,设为k,那么k+1~i这一段便是最大值。

    为了符合题目条件中要满足一段中数的个数大于p,for循环枚举从p开始。

关于题目的某些梗:

    在比赛结束以后,终于弄清楚了他们那么快提交的原因。原来是前一天单调队列专题里的某题,于是,复制粘贴?无奈我做太慢。

代码如下:

#include#includeusing namespace std;int n,q,p;long long k[100005],ans=100000000000000,sum[100005],a[100005];int main(){freopen("2136.in","r",stdin);freopen("2136.out","w",stdout);ios::sync_with_stdio(false);cin>>n>>p>>q;for(int i=1;i<=n;i++) {cin>>a[i];sum[i]=sum[i-1]+a[i];}ans=-ans;int head=1,tail=0;for(int i=p;i<=n;i++){while(head<=tail&&sum[k[tail]]>=sum[i-p]) tail--;tail++;k[tail]=i-p;while(head<=tail&&k[head]

Bug2

题目描述

Bug2

之前的战斗使虫族的建筑遭到破坏,Bug需要组织大家一起重建被破坏的建筑。不过,为了合理分配任务,他需要仔细地考虑虫族的关系网络。

虫族的基地组织是一棵有根树,基地可看作是树的节点。每个基地i都有自身的影响力ai。树的边有一定的长度,记dis(u,v)表示基地u和基地v的距离。

关系网络的复杂性体现在两个基地可能会有控制的关系,我们说基地u控制基地v当且仅当:

uv的祖先;

dis(u,v)≤aV

//注意是child能影响到fatherfather才能控制child

Bug为了合理分配每个基地的任务,需要了解每个基地控制多少个基地。作为虫族总参谋,你能帮帮他吗?

输入格式

第一行一个正整数N,表示基地的个数。

第二行N个整数,第i个表示ai1 ≤ ai ≤ 109

接下来N-1行,每行两个整数a,b。输入的第2+i行,表示基地i+1和基地a之间有一条边,且边的长度为b1 ≤ a ≤ i1 ≤ b ≤ 109

输出格式

一行,N个整数,第i个表示基地i控制多少个基地。

输入样例1

5
2 5 1 4 6
1 7
1 1
3 5
3 6

输出样例1

1 0 1 0 0

输入样例2

5
9 7 8 6 5
1 1
2 1
3 1
4 1

输出样例2

4 3 2 1 0

测试点

N

1~2

≤1 00

3~4

≤1 000

5~10

≤100 000

前言:

    前面送上两个稍微比较水的题目,接下来的题就不是那么简单的了。

题目大意:

    题目比较复杂,我一直理解错题目的意思,1、以为是要由能够影响到即可控制对方  2、以为是由祖先影响到child才行。

     实际上,是由child能影响到祖先,才可以。

     在比赛的时候,我已经被绕晕了。

部分分:

    60分 暴力(无差分)。

正解:

    跑一次dfs,把经过的点加入栈,这样一来,就可以保证栈里面的数都是当前now的祖先。在这里,可以用二分来找到可以影响到哪些祖先。越早入栈的祖先就越距离越远。如图:

    1一定会比2先入栈,而距离3越远的也是1。所以可以用二分找到距离最远的可以影响到的祖先。假设图中3最远可以影响到1,于是给21的ans++。由于数据范围,如果经过这样子逐个增加,一定会超时。

    有一种很特别的方法,举个例子:加入有n个篮子,要在第2~4个篮子里放一个水果 , 再在第3~7个篮子里放多哦一个水果。

    有什么好办法呢?我们先在2篮子那里标记为1,意思为后面的都加上1,可是这样4以后的数字就都不符合题意了,所以在5的地方标记-1。同理在3的地方标记为1,8标记为-1。

 

    在这样的基础上,扫一次for循环,求sum,会有什么样的结果呢?


    正好是每个篮子的果子数。成功地把O(n^2)优化为O(n)。

    同样的,也可以用这种方法算出每一个点被几个点影响到。在最近的可以被影响到的点+1,在最近的不可以被影响到的点-1。

    最后再跑一次dfs,计算出正确的ans即可。

另一种方法:

    前面所说的二分是yhf同学的方法,lhf同学还讲了一种倍增法,但是我不是很能理解,倍增的时间复杂度比较高。

PS:

    纠正一下,听同学lkb说,倍增的时间复杂度其实是差不多的,并且倍增的方法是通用的,而栈的方法有些题目不一定适用


代码如下:

#include#include#includeusing namespace std;const int maxn=100005;long long x,y,nowsum,n,num,p[maxn],a[maxn],ans[maxn],sum[maxn],k[maxn];bool vv[maxn];struct tt{int to,l;};vectorq[maxn];void dfs(long long now,long long from){if(now!=1){a[++num]=from;int lo=0,hi=num+1;while(lo+1>n;for(int i=1;i<=n;i++) cin>>p[i];for(int i=2;i<=n;i++){cin>>x>>y;q[x].push_back(tt{i,y});}dfs(1,0);for(int i=1;i<=n;i++) vv[i]=false;dfs2(1);for(int i=1;i<=n;i++) cout<

圆括号

题目描述

慧音正在教妹红什么是合法括号序列:
空字符串是一个合法括号序列。
如果XY都是合法括号序列,那么XY(即字符串Y拼在字符串X的后面所组成的新的字符串)是一个合法括号序列。
如果X是合法括号序列,那么 (X)也是合法括号序列(即在X的左边加一个左括号,右边加一个右括号)
所有的合法括号序列都可以用~的规则所构造出来。
现在妹红得到了两个括号序列(注意,不一定合法),记为XY,她想构造出一个新的括号序列Z。她每一次会将XY的最左边的字符加进Z的末尾,然后将其从XY中删去,直到XY都为空。她问慧音有多少种选择方案可以让Z是一个合法括号序列。两种选择方案不同是指至少存在一个位置k,使得第一种方案中Z的第k位从X得来,而在第二种方案中Z的第k位从Y得来(不是指两种方案的Z不同)。方案数mod 1000000007(10^9+7)
妹红觉得很好玩,于是她决定再随机生成偶数个括号序列,向慧音多次询问(即多组测试数据)。然而慧音并不知道怎么做,于是她交给了学OI的你。

输入格式 1989.in

第一行一个整数T,表示询问组数。
接下来每组询问有两行,每行一个字符串,分别代表题目中的XY

输出格式 1989.out

T行,每行一个整数,代表生成合法括号序列的方案数。

输入样例 1989.in

6
(()
())
(
)
(((((
)))))
()(()
))((())
()()()()()()()()()()()()()()()
()()()()()()()()
())())))
))((((

输出样例 1989.out

19
1
42
10
493841617
0


第一组询问的19种方法:红色代表来自X,蓝色代表来自Y

【数据范围】
T<=8
XY长度不超过50

当时的情况:

    看到这题就懵逼了,一直没有理解什么是合法括号序列。题目似乎也没有明确说呀。。说白了就是没怎么看懂题目。后来wyy给我解释,所谓的合法括号序列就是每一个左括号都有一个右括号可以对应。

    即使知道了题目的意思,但还是比较蒙,就算是用暴力也不知道该从何下手为好。

正解:

    DP。是一个比较简单的DP,但是对于我这个DP很差的人来说,还是比较困难。f[i][j]表示的状态为,从第一个字符串里面选i个从第2个字符串里面选j个的方案数,并且保证左括号的

数量比右括号的多。

    可以设一个a记录左括号的数量,设一个b记录右括号的数量,如果左括号的数量比右括号的小,那么这个状态是不合法的,方案数为0,continue。

    状态转移方程为f[i][j]=f[i-1][j]+f[i][j-1]。就是可以由第一个字符串取也可以由第二个字符串取。

注意:

    但是注意,在双重for循环中,扫过一次j,但是在第二次for循环会重复添加第二个字符串的左括号数量和右括号数量。所以,每一次外围(i)for循环结束以后,再扫一次j(第二个字符串),再减回刚刚加过的部分,这样子就不会影响到后面了。

    或者也可以用部分和的方法记录括号的数量,我没试过。

代码如下:

#include#include#include#includeusing namespace std;int r,a,b;long long f[55][55];const int mod=1000000007;string x,y;int main(){freopen("1989.in","r",stdin);freopen("1989.out","w",stdout);cin>>r;while(r){r--;int a=0,b=0;cin>>x>>y;for(int i=0;i<=x.size();i++)for(int j=0;j<=y.size();j++) f[i][j]=0;f[0][0]=1;int size1=x.size(),size2=y.size();for(int i=0;i<=size1;i++){if(i>=1){if(x[i-1]=='(') a++;else b++;}for(int j=0;j<=size2;j++){if(i==j&&j==0) continue;if(j>=1){if(y[j-1]=='(') a++;else b++;}if(a=1) f[i][j]=(f[i-1][j]+f[i][j])%mod;if(j>=1) f[i][j]=(f[i][j]+f[i][j-1])%mod;}if(i!=size1){for(int j=1;j<=size2;j++){if(y[j-1]=='(') a--;else b--;}}}if(a==b) cout<

魔法练习

题目描述

在第四届圣杯之战中,时臣其实是预见到麻婆和金闪闪会背叛他的。所以在正式开战前,时臣决定传授祖传宝石魔法给凛,就给了凛魔法图鉴。
在魔法图鉴中,每条魔法都有一个密钥,是一个由“(”“)”“?”组成的魔法符文。时臣同时教凛密钥的破解方法:对于第i个为“?”的符文,可以输入魔力使其变为“(”,或输入的魔力使其变为“)”。只要最终魔法符文都变为合法,匹配的括号符文,就可以瞬间学会该魔法。
凛当然想学会全部魔法,但她还是个小萝莉,魔力有限,她希望,能用最小的魔力,学习全部的魔法(使符文变成合法的括号匹配序列)。


对于40%的数据,n ≤ 100
对于60%的数据,n ≤ 1000
对于100%的数据,n ≤ 100000n为偶数(n为字符串的长度)

输入格式 2088.in

这题目包含多组测试数据(不超过10组)。
每组测试数据,第一行为一个没有空格,偶数长度的字符串,保证由“(”“)”“?”组成,长度不超过100000。然后接下来的m行(m为字符串中“?”的个数),每行有两个整数AiBi(1≤AiBi ≤1000000),分别代表将第i个符文转换为“(”“)”所需的魔力。

输出格式 2088.out

若有解,输出所需最小魔力;若无解,输出-1

输入样例 2088.in

(??)
1 2
2 8

输出样例 2088.out

4

部分分:

    60分 DP

部分分做法:

    之所以要讲部分分做法,是因为这个做法不是水分,还是有点意思的。

    和上一题的做法有点相似,两题有所关联。状态f[i][j]为已经选了前i位,左括号与右括号的差为j。很容易得出状态转移方程:现在取左括号与现在取右括号的min。

    取左括号的情况为f[i-1][j-1]+a[i](因为左括号多了一个,所以左右两个的差值要减一)右括号:f[i-1][j+1]+b[i] 两边的值取Min。

    然而,我还没写出代码,代码略。

讲解后的自行探索:

    我试过两种,不过有一种没有成功,也就是在完成正解之前的一种有漏洞的方法。

    先把所有题目给出的括号置为‘?’,并把所有的左括号都弄为0,右括号置为oo,一路扫下来,用线段树保存前一段中左括号的最小值。同样是用a记录左括号的个数,用b记录右括号的个数。

    如果当a>=b时,无论左右括号哪个比较小,都是可以选的。但是如果a<b,那么一定只能选左括号,于是用for循环看看前面最小的左括号。

    看似很靠谱,但是到最后是无法保证左括号的个数和右括号的个数相等。

正解:

   贪心+优先队列。参考了同学的方法,由于步骤太过于繁琐,标号比较明确。

    1、可以不必把所有括号置为‘?’。

    2、在输入的时候,用k数组记录下左括号数(a)减去右括号数(b)的差值。

    3、假设所有的都为右括号,先把ans加上所有的b。

    4、再开一个临时变量x,为当前ab的差值。

    5、开一个小根堆xiao。

    6、是左括号x++,右括号反之。如果是问号,便把当前这组问号ab(k)的差值存进小根堆里。

    7、如果此时左括号已经比右括号少了,即x=-1,那么便要从前面的问号,也就是小根堆里面存的值里取一个最小的差值加入ans,而小根堆中的top,就是最小的值。

    8、验证一下这样加的正确性:小根堆中存的是问号的k=a-b,而ans一开始是所有b的总和,ans+k=ans+a-b,正好是加上左括号的值,同时把原来ans中存有的右括号消去。

    9、如果此时优先队列为空,也就是说明前面没有问号,那么直接return ans=-1。否则,做上面的加法运算。记得把x改为1。为什么是改为1而不是0,举个例子就可以知道了。本来的a=2,b=3,x=-1;后来改为a=3,b=2,x=1。

    10、被修改的括号就失去了价值,弹出优先队列

    11、最后还有一种特殊情况,即为全部改完以后,a和b不相等,此时也是不合法的。如果没有加这个特判,只有90分。

    除此之外,也可以用线段树,不过线段树比优先队列更为繁琐,所以我也没有写对。

代码如下:
#include#include#include#include#include #includeusing namespace std;const int maxn=100005;long long a,b,ans,c,size,k[maxn];string s;priority_queue , greater > xiao;//小根堆 void solve(){int x=0,c=0;for(int i=0;i>s){size=s.size();ans=0,c=0;memset(k,0,sizeof(k));while(!xiao.empty()) xiao.pop();for(int i=0;i>a>>b;k[++c]=a-b;ans+=b;}}solve();cout<


原创粉丝点击