Index
2019.08.15
说在前面
写博客咯
还剩85天。
变换队形
(transitioning.cpp/c/pas)
【问题描述】
有一个调皮的班级,他们上体育课,现在排成了n行n列的一个矩形。每个人却不是面向老师,而是面向左侧或右侧。例如:
RLR
RRL
LLR
因为老师是新来的,所以还叫不出学生的名字。他每次都是朝着一列或一行喊:向后转。这一行或这一列的L就全部变成R,R就全部变成L。因为不能让所有学生都朝一个方向,所以体育老师退而求其次,他可以允许一个人和其他人方向相反。体育老师的指令没有次数限制,他也可以对同一行或同一列进行多次发令。请找出这样的一个学生。如果最后无法使得只有一个学生和其他人方向相反,则输出“-1”。如果存在方案,则输出那一个学生的行号和列号(均从1开始)。如果有多种方案,则输出行号最小的那一个学生,如果多个学生的行坐标相等,则输出列号最小的那一个。
【输入格式】
输入文件名为 transitioning.in。
第一行一个整数,表示n。
接下来是n行,每行n个字符,每一个字符为L或R。
【输出格式】
输出文件名为 transitioning.out。
输出文件只有 1 行,表示那个讨厌的学生的行号和列号。
如果没有,则输出-1。
【输入输出样例 】
输入样例 | 输出样例 |
---|---|
R L R R R L L L R \begin{array}{lcrlcr} RLR \\ RRL \\ LLR \end{array} RLRRRLLLR | 1 1 1~~~1 1 1 |
【输入输出样例说明】
在这个例子中,位于第1行第1列(左上角)的学生是那个令人讨厌的学生,因为老师可以喊叫第2行和第3列向后转,只有这一个学生和其他学生朝向不同。
以上是魔改后题面,以下是原题面。
[USACO19OPEN]Left Out
题目描述
Farmer John正在尝试给他的牛群拍照。根据以往的经验,他知道这一工作往往结果不怎么样。
这一次,Farmer John购买了一台昂贵的无人机,想要拍一张航拍照。为了使照片尽可能好看,他想让他的奶牛们在拍照时都朝向同一个方向。奶牛们现在在一块有围栏的草地上排列成 N \times N N×N ( 2 \leq N \leq 1000 2≤N≤1000 )的方阵,例如:
RLR
RRL
LLR
这里,字符’R’表示一头朝右的奶牛,字符’L’表示一头朝左的奶牛。由于奶牛们都挤在一起,Farmer John没办法走到某一头奶牛面前让她调转方向。他能做的只有对着某一行或某一列的奶牛喊叫让她们调转方向,使得被叫到的这一行或列内的所有L变为R,R变为L。Farmer John可以对任意多的行或列发号施令,也可以对同一行或列多次发令。
就如同Farmer John想象的,他发现他不可能让他的奶牛们都朝向同一个方向。他最多能做的是让所有奶牛中除了一头之外都朝向相同的方向。请找出这样的一头奶牛。
输入格式
输入的第一行包含 N N 。以下 N N 行描述了奶牛方阵的第 1 \ldots N 1…N 行,每行包含一个长度为 N N 的字符串。
输出格式
输出一头奶牛的行列坐标,满足这头奶牛被调转方向的话,Farmer John就可以使他的所有奶牛都朝向同一个方向。如果不存在这样的奶牛,输出-1。如果存在多头这样的奶牛,输出其中行坐标最小的,如果多头这样的奶牛具有相同的行坐标,输出其中列坐标最小的。
输入输出样例
输入
3
RLR
RRL
LLR
输出
1 1
说明/提示
在这个例子中,位于第1行第1列(左上角)的奶牛是那头令人讨厌的奶牛,因为Farmer John可以喊叫第2行和第3列来让所有奶牛都面向左侧,只有这一头奶牛面向右侧。
分析
可以把题意抽象为如下:有n*n的01矩阵,每次操作可以将一行或一列的所有元素取反,问经过若干次操作之后,是否能使矩阵只有一个1或只有一个0,若能,则输出这个数字的坐标,反之输出-1。
银组第一题,普及+/提高题。
我没有做出来。
考试的题解是如下说的,如图,用颜色代表0和1。
假设我们要让最后一排的颜色统一。
首先我们用竖排的变化让最后一排都变为橙色,那么之后我们就不能再变化竖排了,这样的话最后一排的统一就会被破坏,所以打叉的那两个(只依靠横排变化)是不可能与这一排的其他人统一的,因此就会违法“只有一个人和大家方向不同”,因此impossible。
如果我们想统一的不是最后一排,同理。
其实本质相同,只是让最后一排统一,就可以保证要求的那个人不在最后一排,答案稍稍更优。
当然,这只是我们思考的一步,我们迈出了一步再来看能不能达到期望的结局,然后在退一步回来推理怎样的原图能到达期望的结局。
注意,我们已经统一了最后一排,所以不进行竖变化。
那么只靠横变化能够将怎样的图变为目标图呢?
答案是比较显然的,就是有且仅有一个横排其中较少的数量为1个,其余的横排都是相同的。
也就是说:
①不能全都是同色横排;
②不能有一个横排,异色的格子有两个或两个以上;(异色指较少数量的格子)
③必须有一个横排其中有一个异色格子;
④不能有两个或两个以上个横排其中有一个异色格子。
所以说我们在把最后一行统一以后,枚举剩下的横排,数它们其中的异色块数
还有一种情况,是我一开始随手画图画出来的,是上述方法不能解决的。
为什么随手画会画出特例啊啊啊
上面那个引用块说“这样就可以保证答案不在最后一排”。
这就是这个算法的漏洞。
我随手画的图把std爆破了。
就是答案在最后一排的情况。
这样的话,统一了最后一排就错了;
不过这样的话答案的上方全都是单个的异色块,也很好特判。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1024;
char mp[MAXN];
int n,R,ansx,ansy,cnt,is[MAXN][MAXN];
int main()
{
scanf("%d",&n);
if(n==1) return puts("1 1"),0;
for(int i=1;i<=n;i++)
{
scanf("%s",mp+1);
for(int j=1;j<=n;j++) is[i][j]=mp[j]=='R';
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(is[n][j]) is[i][j]^=1;
for(int i=1;i<=n && cnt<=n;i++)
{
R=0;
for(int j=1;j<=n;j++) R+=is[i][j];
if(R!=0 && R!=n && R!=1 && R!=n-1) {cnt=2147484647; break;}
if(R==1 || R==n-1)
{
cnt++;
for(int j=1;j<=n;j++)
if(is[i][j]==(R==1))
{
if(cnt>1) {if(ansy!=j) cnt=2147483647;}
else ansx=i,ansy=j;
break;
}
}
}
if(cnt==1 || cnt==n-1) printf("%d %d\n",cnt==n-1?n:ansx,ansy);
else puts("-1");
return 0;
}
跨栏
(jump.cpp/c/pas)
【问题描述】
在过去,校长曾经构思了许多新式学生运动项目的点子,其中就包括学生障碍赛,是学生们在赛道上跑越障碍栏架的竞速项目。他之前对推广这项运动做出的努力结果喜忧参半,所以他希望在他的操场上建造一个更大的学生障碍赛的场地,试着让这项运动更加普及。校长为新场地精心设计了N个障碍栏架,编号为1…N(2≤N≤
1
0
5
10^5
105),每一个栏架都可以用这一场地的二维地图中的一条线段来表示。这些线段本应两两不相交,包括端点位置。
不幸的是,校长在绘制场地地图的时候不够仔细,现在发现线段之间出现了交点。然而,他同时注意到只要移除一条线段,这张地图就可以恢复到预期没有相交线段的状态(包括端点位置)。
请求出校长为了恢复没有线段相交这一属性所需要从他的计划中删去的一条线段。如果有多条线段移除后均可满足条件,请输出在输入中出现最早的线段的序号。
【输入格式】
输入的第一行包含N。余下N行每行用四个整数x1 y1 x2 y2表示一条线段,均为至多 1 0 9 10^9 109的非负整数。这条线段的端点为(x1,y1)和(x2,y2)。所有线段的端点各不相同。
【输出格式】
输出在输入中出现最早的移除之后可以使得余下线段各不相交的线段序号。
【输入输出样例】
jump.in
4
2 1 6 1
4 0 1 5
5 6 5 5
2 7 1 3
jump.out
2
提示
注意:由于线段端点坐标数值的大小,在这个问题中你可能需要考虑整数类型溢出的情况
以上是魔改题面,以下是原题面:
[USACO19]Cow Steeplechase II
题目描述
在过去,Farmer John曾经构思了许多新式奶牛运动项目的点子,其中就包括奶牛障碍赛,是奶牛们在赛道上跑越障碍栏架的竞速项目。他之前对推广这项运动做出的努力结果喜忧参半,所以他希望在他的农场上建造一个更大的奶牛障碍赛的场地,试着让这项运动更加普及。
Farmer John为新场地精心设计了N个障碍栏架,编号为 1 … N ( 2 ≤ N ≤ 1 0 5 ) 1 \ldots N(2 \leq N \leq 10^5) 1…N(2≤N≤105),每一个栏架都可以用这一场地的二维地图中的一条线段来表示。这些线段本应两两不相交,包括端点位置。
不幸的是,Farmer John在绘制场地地图的时候不够仔细,现在发现线段之间出现了交点。然而,他同时注意到只要移除一条线段,这张地图就可以恢复到预期没有相交线段的状态(包括端点位置)。
请求出Farmer John为了恢复没有线段相交这一属性所需要从他的计划中删去的一条线段。如果有多条线段移除后均可满足条件,请输出在输入中出现最早的线段的序号。
输入格式
输入的第一行包含 N 。余下 N 行每行用四个整数 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 x1,y1,x2,y2表示一条线段,均为至多 1 0 9 10^9 109的非负整数。这条线段的端点为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)) 。所有线段的端点各不相同。
输出格式
输出在输入中出现最早的移除之后可以使得余下线段各不相交的线段序号。
输入输出样例
输入 #1
4
2 1 6 1
4 0 1 5
5 6 5 5
2 7 1 3
输出 #1
2
说明/提示
注意:由于线段端点坐标数值的大小,在这个问题中你可能需要考虑整数类型溢出的情况。
分析
首先,既然题目保证删掉某一条边之后,剩下的边都不相交,那么就一定有如下性质:
如
果
有
两
条
线
段
相
交
,
那
么
答
案
一
定
是
其
中
之
一
。
\Large \bold 如果有两条线段相交,那么答案一定是其中之一。
如果有两条线段相交,那么答案一定是其中之一。
我居然忽略了这么显然的性质。
于是就可以
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)枚举点对了。
然后考虑优化。
把所有端点按照从左到右从下到上排序,
以下内容博主咕掉了,详情请参见https://blog.csdn.net/c20180602_csq/article/details/99655074这篇大佬的博客。
bzoj 4972 [Lydsy1708月赛] 小Q的方格纸
Time Limit: 10 Sec Memory Limit: 256 MB
Description
方格纸与草稿纸一样,都是算法竞赛中不可或缺的重要工具。身经百战的小Q自然也会随身带着方格纸。小Q的方格纸有 n n n行m列,一共 n ∗ m n*m n∗m个方格,从上到下依次标记为第 1 , 2 , . . . , n 1,2,...,n 1,2,...,n行,从左到右依次标记为第 1 , 2 , . . . , m 1,2,...,m 1,2,...,m列,方便起见,小Q称第i行第j列的方格为 ( i , j ) (i,j) (i,j)。小Q在方格纸中填满了数字,每个格子中都恰好有一个整数 a i , j a_{i,j} ai,j。小Q不喜欢手算,因此每当他不想计算时,他就会让你帮忙计算。小Q一共会给出q个询问,每次给定一个方格 ( x , y ) (x,y) (x,y)和一个整数 k ( 1 < = k < = m i n ( x , y ) ) k(1<=k<=min(x,y)) k(1<=k<=min(x,y)),你需要回答由 ( x , y ) , ( x − k + 1 , y ) , ( x , y − k + 1 ) (x,y),(x-k+1,y),(x,y-k+1) (x,y),(x−k+1,y),(x,y−k+1)三个格子构成的三角形边上以及内部的所有格子的a的和。
Input
第一行包含6个正整数
n
,
m
,
q
,
A
,
B
,
C
(
1
<
=
n
,
m
<
=
3000
,
1
<
=
q
<
=
3000000
,
1
<
=
A
,
B
,
C
<
=
1000000
)
n,m,q,A,B,C(1<=n,m<=3000,1<=q<=3000000,1<=A,B,C<=1000000)
n,m,q,A,B,C(1<=n,m<=3000,1<=q<=3000000,1<=A,B,C<=1000000)
其中
n
,
m
n,m
n,m表示方格纸的尺寸,q表示询问个数。
为了防止输入数据过大,a和询问将由以下代码生成:
unsigned int A,B,C;
inline unsigned int rng61(){
A ^= A << 16;
A ^= A >> 5;
A ^= A << 1;
unsigned int t = A;
A = B;
B = C;
C ^= t ^ A;
return C;
}
int main(){
scanf("%d%d%d%u%u%u", &n, &m, &q, &A, &B, &C);
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
a[i][j] = rng61();
for(i = 1; i <= q; i++){
x = rng61() % n + 1;
y = rng61() % m + 1;
k = rng61() % min(x, y) + 1;
}
}
Output
为了防止输出数据过大,设
f
i
f_i
fi表示第
i
i
i个询问的答案,则你需要输出一行一个整数,即:
(
∑
i
=
1
q
23
3
q
−
i
∗
f
i
)
m
o
d
2
32
(\sum_{i=1}^q 233^{q-i}*f_i) mod~2^{32}
(∑i=1q233q−i∗fi)mod 232
Sample Input
3 4 5 2 3 7
Sample Output
3350931807
本题没有被魔改。
题意分析
有一个n*m的方格,每次给定一个x,y,k,求 ( x , y ) , ( x − k + 1 , y ) , ( x , y − k + 1 ) (x,y),(x-k+1,y),(x,y-k+1) (x,y),(x−k+1,y),(x,y−k+1)三个格子构成的三角形的所占格子的和。
首先如上图,我们把这个格子三角形简化为线段的三角形。
我们假设我们要求那个红色边框三角形的和,也就是下图的橙色三角形。
那么如易知,橙色=红色长方形-蓝色长方形-绿色梯形=红色长方形-蓝色长方形-(粉色三角形-紫色三角形)。
红色和蓝色长方形非常容易用前缀和预处理出来,之后粉色和紫色的三角形是同一类的stand……shape呢,也可以轻松地预处理出来。
粉色三角=红色三角+橙色长方=红色三角+(紫色长方-绿色长方)
这道题就轻松的做了出来。
(但是看我画的图就非常不轻松,博主艺术天分为-2147483647,上色r智,请见谅,将就看就好了)
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=3030;
int n,m,q,x,y,k;
unsigned int A,B,C,t,a[MAXN][MAXN],sum[MAXN][MAXN],sig[MAXN][MAXN],ret,ans;
inline unsigned rng61()
{
A^=A<<16,A^=A>>5,A^=A<<1;
t=A,A=B,B=C,C^=t^A;
return C;
}
int main()
{
scanf("%d %d %d %u %u %u",&n,&m,&q,&A,&B,&C);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=rng61();
for(int i=1;i<=max(n,m);i++)
for(int j=1;j<=max(n,m);j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
for(int i=1;i<=max(n,m);i++)
for(int j=max(n,m);j>=1;j--)
sig[i][j]=sig[i-1][j+1]+sum[j][i]-sum[j-1][i];
while(q--)
{
x=rng61()%n+1,y=rng61()%m+1,k=rng61()%min(x,y)+1;
ret=(sum[x][y]-sum[x-k][y])-(sig[y-1][x-k+1]-sig[y>k?y-k-1:0][x+1]);
ans=ans*233+ret;
}
printf("%u\n",ans);
return 0;
}