NOIP模拟 6.26

T1 子矩阵

题目描述

小A有一个N×M的矩阵,矩阵中1~N*M这(N*M)个整数均出现过一次。现在小A在这个矩阵内选择一个子矩阵,其权值等于这个子矩阵中的所有数的最小值。小A想知道,如果他选择的子矩阵的权值为i(1<=i<=N×M),那么他选择的子矩阵可能有多少种?小A希望知道所有可能的i值对应的结果,但是这些结果太多了,他算不了,因此他向你求助。

输入格式:

第一行,两个整数N, M。

接下来的N行,每行M个整数,表示矩阵中的元素。

输出格式:

N×M行,每行一个整数,其中第i行的整数表示如果小A选择的子矩阵权值为i,他选择的子矩阵的种类数。

输入样例#1:

2 3

2 5 1

6 3 4

输出样例#1:

6

4

5

1

1

1

题解:

部分枚举,扫描列的典型模型

枚举两个行数i,j(规定i <= j),寻找[i,j]行内的矩形

递推可得[i,j]行内每一列的最小值

维护单调递增的栈,计算L[k](在[i,j]行内选择第k列,则向左最远能够选到L[k]列),R[i](在[i,j]行内选择第k列,则向右最远能够选到R[k]列)

乘法原理得到左右行数在[i,j]内的、包含第k列的子矩阵

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
inline void read(int &x){x = 0;char ch = getchar();char c = ch;while(ch > ‘9‘ || ch < ‘0‘)c = ch, ch = getchar();while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘,ch = getchar();if(c == ‘-‘)x = -x;}
inline void swap(int &a, int &b){int tmp = a;a = b;b = tmp;}
inline int max(int a, int b){return a > b ? a : b;}
inline int min(int a, int b){return a < b ? a : b;}

const int INF = 0x3f3f3f3f;
const int MAXN = 5000 + 10;
const int MAXM = 100000;

int n,m,num[MAXN][MAXN];
int stack[MAXM], top;
int mi[MAXM];
int L[MAXM],R[MAXM];
int ans[MAXM];

int main()
{
	read(n);read(m);
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= m;++ j)
			read(num[i][j]);
	//枚举上面的行i
	for(register int i = 1;i <= n;++ i)
	{
		//注意清为最大值
		memset(mi, 0x3f, sizeof(mi));

		//枚举i以下的行j
		for(register int j = i;j <= n;++ j)
		{
			 //求得行[i,j]范围内的每一列的最小值
			for(register int k = 1;k <= m;++ k)
				mi[k] = min(mi[k], num[j][k]);

			//正向扫描求R,维护一个递增(或相等)单调栈
			for(register int k = 1;k <= m;++ k)
			{
				while(top && mi[k] < mi[stack[top]])
				{
					R[stack[top]] = k - 1;
					-- top;
				}
				stack[++top] = k;
			}
			while(top)
			{
				R[stack[top]] = m;
				-- top;
			}

			//反向扫描求L,维护一个递增(或相等)单调栈
			 for(int k = m;k >= 1;k --)
			 {
			 	while(top && mi[k] < mi[stack[top]])
			 	{
			 		L[stack[top]] = k + 1;
					-- top;
			 	}
			 	stack[++top] = k;
			 }
			 while(top)
			 {
			 	L[stack[top]] = 1;
			 	-- top;
			 }

			 //扫描列,累加答案
			 for(register int k = 1;k <= m;k ++)
			 {
			 	ans[mi[k]] += (k - L[k] + 1) * (R[k] - k + 1);
			 }
		}
	}
	register int tmp = n * m;
	for(register int i = 1;i <= tmp;++ i)
	{
		printf("%d\n", ans[i]);
	}
	return 0;
}

T2 序列操作

题目描述

小B有一个整数序列a[1..N],初始时序列中所有元素均为0。他会在序列上进行下面两种操作,操作共M个:

1、A l r x:将a[l..r]均加上x。

2、Q l r:询问a[l..r]中的最大值。

输入格式:

第一行,两个整数N, M。

接下来的M行,每行一个操作。

输出格式:

设询问操作有T个,则输出T行,每行一个整数,表示询问操作对应的答案。

输入样例#1:

5 5

A 1 4 1

A 2 5 2

Q 1 4

A 3 4 -2

Q 3 5

输出样例#1:

3

2

题解:线段树裸题

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <algorithm>
inline void read(int &x){x = 0;char ch = getchar();char c = ch;while(ch > ‘9‘ || ch < ‘0‘)c = ch, ch = getchar();while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘,ch = getchar();if(c == ‘-‘)x = -x;}
inline void swap(int &a, int &b){int tmp = a;a = b;b = tmp;}
inline int max(int a, int b){return a > b ? a : b;}
inline int min(int a, int b){return a < b ? a : b;}

const int INF = 0x3f3f3f3f;
const int MAXN = 100000 + 10;
const int MAXM = 100000 + 10;

int sgtmax[MAXN];
int sgtlazy[MAXN];
int n,m;

inline void putdown(int& o, int& l, int& r)
{
	sgtmax[o << 1] += sgtlazy[o];
	sgtlazy[o << 1] += sgtlazy[o];
	sgtlazy[o << 1 | 1] += sgtlazy[o];
	sgtmax[o << 1 | 1] += sgtlazy[o];
	sgtlazy[o] = 0;
}

void modify(int ll, int rr, int k, int o = 1, int l = 1, int r = n)
{
	if(ll <= l && rr >= r)
	{
		sgtmax[o] += k;
		sgtlazy[o] += k;
		return;
	}
	int mid = (l + r) >> 1;
	if(sgtlazy[o])putdown(o, l, r);
	if(mid >= ll)modify(ll, rr, k, o << 1, l, mid);
	if(mid < rr)modify(ll, rr, k, o << 1 | 1, mid + 1, r);
	sgtmax[o] = max(sgtmax[o << 1], sgtmax[o << 1 | 1]);
}

int ask(int ll, int rr, int o = 1, int l = 1, int r = n)
{
	if(ll <= l && rr >= r)
	{
		return sgtmax[o];
	}
	int mid = (r + l) >> 1;
	if(sgtlazy[o])putdown(o, l, r);
	int ans = -INF;
	if(mid >= ll)ans = max(ans, ask(ll, rr, o << 1, l, mid));
	if(mid < rr)ans = max(ans, ask(ll, rr, o << 1 | 1, mid + 1, r));
	return ans;
}

int main()
{
	read(n);read(m);
	register int i,tmp1,tmp2,tmp3;
	register char c;
	for(i = 1;i <= m;i ++)
	{
		c = getchar();
		while(c != ‘A‘ && c != ‘Q‘)c = getchar();
		if(c == ‘A‘)
		{
			read(tmp1);read(tmp2);read(tmp3);
			modify(tmp1, tmp2, tmp3);
		}
		else
		{
			read(tmp1);read(tmp2);
			printf("%d\n", ask(tmp1, tmp2));
		}
	}
	return 0;
}

T3 议案决定

题目描述

小C在玩一个游戏,在游戏中他扮演国王的角色。国王手下有M个大臣,他们有一天对国王要处理的N件事务进行投票。 每个大臣可以对两件事务进行投票(赞成或反对),格式如下:x c_x y c_y(x, y为整数,c_x, c_y为“Y”(表示赞成)或“N”(表示反对)(不含双引号),表示这个大臣对事务x的态度为c_x,对事务y的态度为c_y)。这些大臣非常难以对付,如果国王的决定和某个大臣的两个意见都不同,那么这个大臣就会离开国王。小C认为不能让任何一个大臣离开国王,否则国王就无法正常地处理自己的事务。 请你帮助小C做个决定。

输入格式:

第一行,两个整数N, M。

接下来的M行,每行表示一个大臣的投票。

输出格式:                      

如果小C无论如何都只能让至少一个大臣离开国王,则输出“IMPOSSIBLE”(不含双引号),否则输出一个长度为N的字符串 ,如果第i件事务必须赞成,则第i个字符为“Y”;如果第i件事务必须反对,则第i个字符为“N”,否则第i个字符为“?”。

输入样例#1:

3 4

1 Y 2 N

1 N 2 N

1 Y 2 Y

输出样例#1:

YN?

题解:

经典2-SAT问题

点u->v代表u发生则v必然发生

每一个条件可以拆为这样的两个条件

如1 Y 2 N  对应的是  1N->2N   2Y->1Y

我们把1Y  1N称为对立节点

符合条件的情况是,从节点i出发找一条路径,路径上不会遇到i的对立节点。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>

const int MAXN = 80000 + 10;

inline void read(int &x){x = 0;char ch = getchar();char c = ch;while(ch > ‘9‘ || ch < ‘0‘)c = ch, ch = getchar();while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘, ch = getchar();if(c == ‘-‘)x = -x;}
inline void read(char& x){x = 0;while(x != ‘Y‘ && x != ‘N‘)x = getchar();}

struct Edge{int u,v,next;}edge[MAXN << 1];
int head[MAXN << 1],cnt;
void insert(int a, int b){edge[++cnt] = Edge{a,b,head[a]};head[a] = cnt;}

int n,m;

int ans[MAXN];

//Y:ou shu N:ji shu 

int match[MAXN << 1];

int dfs(int u)
{
	for(int pos = head[u];pos;pos = edge[pos].next)
	{
		int v = edge[pos].v;
		if(!v || v == 1)continue;
		if(v & 1)
		{
			if(match[v - 1])return 0;
			if(match[v])continue;
			match[v] = 1;
			int tmp = dfs(v);
			if(!tmp) return 0;
		}
		else
		{
			if(match[v | 1])return 0;
			if(match[v])continue;
			match[v] = 1;
			int tmp = dfs(v);
			if(!tmp)return 0;
		}
	}
	return 1;
}

inline int check(int i)
{
	memset(match, 0, sizeof(match));
	match[i] = 1;
	return dfs(i);
}

const char a[3] = {‘?‘, ‘Y‘, ‘N‘};

int main()
{
	read(n);read(m);
	register char c1,c2;register int tmp1, tmp2;
	register int i;
	for(i = 1;i <= m;++ i)
	{
		read(tmp1);read(c1);read(tmp2);read(c2);
		if(c1 == ‘Y‘ && c2 == ‘N‘)
		{
			insert(tmp1 << 1 | 1, tmp2 << 1 | 1);
			insert(tmp2 << 1, tmp1 << 1);
		}
		else if(c1 == ‘Y‘ && c2 == ‘Y‘)
		{
			insert(tmp2 << 1 | 1, tmp1 << 1);
			insert(tmp1 << 1 | 1, tmp2 << 1);
		}
		else if(c1 == ‘N‘ && c2 == ‘N‘)
		{
			insert(tmp2 << 1, tmp1 << 1 | 1);
			insert(tmp1 << 1, tmp2 << 1 | 1);
		}
		else if(c1 == ‘N‘ && c2 == ‘Y‘)
		{
			insert(tmp2 << 1 | 1, tmp1 << 1 | 1);
			insert(tmp1 << 1, tmp2 << 1);
		}
	}

	for(i = 1;i <= n;i ++)
	{
		tmp1 = check(i << 1);
		tmp2 = check(i << 1 | 1);
		if(tmp1 && tmp2)ans[i] = 0;
		else if(tmp1)ans[i] = 1;
		else if(tmp2)ans[i] = 2;
		else
		{
			printf("IMPOSSIBLE");
			return 0;
		}
	}

	for(int i = 1;i <= n;i ++)
	{
		printf("%c", a[ans[i]]);
	}
	return 0;
}
时间: 2024-10-12 19:50:34

NOIP模拟 6.26的相关文章

NOIP模拟17.8.17

NOIP模拟17.8.17 A 小 G 的字符串文件名 输入文件 输出文件 时间限制 空间限制str.pas/c/cpp str.in str.out 1s 128MB[题目描述]有一天,小 L 给小 G 出了这样一道题:生成一个长度为 n 的.全由小写英文字母构成的字符串,只能使用 k 种字母.要求满足:• 字符串中相邻的两个字母不能相同.• 必须出现恰好 k 种不同的字母.这样的合法字符串可能有很多,小 L 让小 G 输出字典序最小的那个.小 G 太笨啦,不会做这道题,希望你帮帮他.[输入格

NOIP模拟 17.8.18

NOIP模拟17.8.18 A.小菜一碟的背包[题目描述]Blice和阿强巴是好朋友但萌萌哒Blice不擅长数学,所以阿强巴给了她一些奶牛做练习阿强巴有 n头奶牛,每头奶牛每天可以产一定量的奶,同时也需要一定量的草作为饲料对于第 i头奶牛来说,它每天可以产 vi升的奶,同时需要 wi千克的草作为饲料现在来自蚯蚓国的九条可怜想借一些奶牛,使借走的这些奶牛每天的总产奶量最大,但九条可怜很穷,每天最多只能提供W千克的草作为饲料,而且她还需要对付跳蚤国的神刀手,所以她把这个问题交给了阿强巴,不不不……阿

NOIP模拟 6.28

NOIP模拟赛6.28 Problem 1 高级打字机(type.cpp/c/pas) [题目描述] 早苗入手了最新的高级打字机.最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧. 请为这种高级打字机设计一个程序,支持如下3种操作: 1.T x:在文章末尾打下一个小写字母x.(type操作) 2.U x:撤销最后的x次修改操作.(Undo操作) (注意Query操作并不算修改操作) 3.Q x:询问当前文章中第x个字母并输出.(Query操作) 文章一开始可以视为空串. [输入格式]

NOIP模拟17.9.21

NOIP模拟17.9.21 1 任务安排manage.in/.out/.cpp1.1 问题描述你有N 个工作,同一时刻只能做一个任务, 其中每个工作有其所需时间, 及完成的Deadline(截止时间), 问要完成所有工作, 最迟要从什么时候开始.你最早可以从时间0 开始工作.1.2 输入格式第一行一个整数N,表示任务数量接下来n 行,每行两个整数,Ti; Si,分别表示该任务的持续时间和截止时间.1.3 输出格式输出一个整数,表示最晚的开始时间,如果不能完成,输出-1.1.4 样例输入43 58

NOIP模拟赛 6.29

2017-6-29 NOIP模拟赛 Problem 1 机器人(robot.cpp/c/pas) [题目描述] 早苗入手了最新的Gundam模型.最新款自然有着与以往不同的功能,那就是它能够自动行走,厉害吧. 早苗的新模型可以按照输入的命令进行移动,命令包括‘E’.‘S’.‘W’.‘N’四种,分别对应东南西北.执行某个命令时,它会向对应方向移动一个单位.作为新型机器人,它可以执行命令串.对于输入的命令串,每一秒它会按命令行动一次.执行完命令串的最后一个命令后,会自动从头开始循环.在0时刻时机器人

NOIP模拟17.9.22

NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥ ??的位置去,但很不幸数轴上有??个区间是禁区,不能进入.青蛙会选择一个长度??,从原点开始每次向右跳长度为??的一段.一路上青蛙会停的位置是0, ??, 2??,…直到跳到了≥ ??的位置,任意一个位置都不能在禁区中.请求出??的最小值,注意??可以是实数.[输入格式]输入文件为susume.in.输入文件的第一行包含两个整数??和??,含义如问题描述中所述.接下来??行,每行描述一个禁区.每行有两个整数

liu_runda 给辣鸡蒟蒻做的 NOIP模拟赛 1.0 第二题 任(duty) 题解

问题 B: 任(duty) 时间限制: 2 Sec  内存限制: 512 MB 题目描述 liu_runda退役之后就失去梦想开始咸鱼生活了- Bilibili夏日画板活动中,所有人都可以在一块画板上进行像素画创作.UOJ群有一群无聊的人决定在画板上创作一个50*50的UOJ的LOGO.如下图. 这块画板实际上是很大的矩形网格.一个网格是一像素. 一个人每三分钟才能画一个像素.所以liu_runda的咸鱼生活非常无聊. 郭神表示他实在是看不下去liu_rudna这只颓狗了,于是随手出了一道神题,

noip模拟测试11

T1:string 第一眼秒出思路,这不就是排序那道题的加强版吗? 然而歪?解复杂度虽然是对的,但常数过大,竟被卡到70 歪?解:(实际上std写的就是这个,但据说std被卡掉了 OAO) 因为字符集很小,所以我们可以把区间排序改为区间查询和覆盖 即:先查询区间内所有字符的个数,再从左端点开始按照大小关系依次将长度为字符个数的区间修改为该字符. 期望复杂度O ( 26*mlogn ),实际复杂度O ( 26*mlogn*(巨大的常数) ) 所以需要一(feng)定(kuang)的卡常 正?解:

[BZOJ入门OJ2092][Noip模拟题]舞会

2092: [Noip模拟题]舞会 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 9  Solved: 5 [Submit][Status][Web Board] Description 学校举行舞会啦,一共有N个人参加,所有人站成一排,从左开始编号,最左边的人编号为1 ,最右边的为N.每个人跳舞的熟练度我们用一个整数表示,第i个人的熟练度为Ai,每次熟 练度最接近的一对相邻男女会出列跳舞,如果有多对那么最左边的那一对会先出列,请你给 出出列跳