今日总结:
爆零了,主要原因在于时间分配不匀,和太过贪心,以及基础知识反应不及时。
第一题我没想出正解,但是-1下来打完有20分,后悔没有骗分。
第二题初做有灵感,线段树映射x轴加速也想过,可惜一直想正解,最后时机没把握住。
第三题的奇怪数据方式和数学公式把我唬住了,没有细看,以为是道数论题,结果前缀后就可以。
总的来说,发挥失常也有,知识不牢也有,需要抓紧时间提升能力。
【[USACO19OPEN]Left Out】
这道题,分析题意,大概就是一个01表格,要将其转化为只有一个为1或只有一个为0的情况,无限次取反,每次只能取反一行或一列,求那个不同的点的位置,且必须最前面的那个。 那么简单分析题意后,就应该知道全取反一遍也可以,所以L和R的意义差不多。
要求最前面那个的话,那么最后一行我们可以先暴力列取反为一样的, 那么前面的少数的如果只有一个,那么他一定是答案,而如果有两个那么一定无解。 因为如果再次进行列取反,那么会导致最后一行混乱。
并且,可以看出选用其他行对全局影响一致,如果担心最后一行也有,那么换一行再来一边就行了,不过好像没有这种数据。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define LL long long
#define M 1005
void read(LL &x){
x=0;LL f=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-f;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
x*=f;
return ;
}
LL n,mp[M][M];
int main(){
//freopen("transitioning.in","r",stdin);
//freopen("transitioning.out","w",stdout);
read(n);
for(int i=1;i<=n;i++){
char c;
for(int j=1;j<=n;j++){
c=getchar();
if(c=='R')
mp[i][j]=1;
}
if(i<n)
c=getchar();
}
for(int i=1;i<=n;i++){
if(mp[n][i])
for(int j=1;j<=n;j++){
mp[j][i]=1-mp[j][i];
}
}
LL a,b;
bool f=0,s=0;
LL tp=0;
for(LL i=n-1;i>0;i--){
LL l=0,r=0;
LL u=0,v=0;
for(LL j=n;j>0;j--){
if(mp[i][j])
r++,u=j;
else
l++,v=j;
}
if(r&&l)
tp++;
if(tp>=2)
s=1;
if(r>1&&l>1){
s=1;
break;
}else if(r==1||l==1){
a=i;
if(r<l)
b=u;
else
b=v;
f=1;
}
}
if(s||!f)
puts("-1");
else
printf("%lld %lld",a,b);
return 0;
}
[USACO19OPEN]Cow Steeplechase II
这道题一看可知是要求出相连的的最前面两个,因为最多3个连在一起,甚至有时候3个也不行,所以求出相连两条即可退出。
首先想到复杂度做法,但明显超时,需要优化,所以采取扫描线进行优化至
。
现将每个点(注意,分开了,但要标记属于那条线)按照x轴从小到大排序,x坐标相同的按y坐标从大到小。用set进行存储,
在左端点出现后,让这条线进入集合,与两边的线进行检查是否相交,注意,set有排序功能,所以你要重载小于,要注意比较方式,求出相邻两条边在同一x坐标y坐标大小,因为这个x坐标会随扫描线移动。
在右端点出现后,让这条线出集合,因为扫描线已经扫过了。这是还要检查一遍左右两条线是否相交
,因为左右可能变化了。
注:这道题需熟练用于set的各种函数。
比较也需要技巧:对立叉乘实验
p1和p2是两条线段,且左端点处于同一位置(0,0)右端点分别为(x1,y1),(x2,y2),
线段p1和p2的叉乘结果为p1Xp2=x1*y2-x2*y1
如若结果大于0,则p2在p1的顺时针180度以内,如果小于零,在逆时针180度以内。
要判断是否交叉,可以取p1线上一端点连上p2线的两个端点,得到两条条线,再分别与p1进行叉乘实验。
再可以取p2线上一端点连上p1线的两个端点,得到两条条线,再分别与p2进行叉乘实验。
就可以知道是否p1两点在p2两边,p2两点在p1两边。
就能得到答案了
上代码:
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
#define LL long long
#define M 100005
void read(LL &x){
x=0;LL f=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-f;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
x*=f;
return ;
}
double x;
LL n,ans1,ans2;
struct edge{
LL x,y,id;
bool operator <(edge b)const{
return x==b.x?y<b.y:x<b.x;
}
}t[M<<1];
struct node{
edge a,b;
LL id;
bool operator ==(node k)const{
return id==k.id;
}
}p[M];
double eval(node op){
if(op.a.x==op.b.x)
return op.a.y;
return op.a.y+(op.b.y-op.a.y)*(x-op.a.x)/(op.b.x-op.a.x);
}
bool operator <(node u,node v){
return u.id!=v.id&&eval(u)<eval(v);
}
set<node>s;
LL sum[M];
LL sign(LL x){
if(!x)
return 0ll;
if(x>0)
return +1ll;
return -1ll;
}
LL operator* (edge u,edge v){
return sign(u.x*v.y-u.y*v.x);
}
edge operator- (edge u,edge v){
edge k1={u.x-v.x,u.y-v.y};
return k1;
}
bool cmp(node u,node v){
edge &a1=u.a,&a2=v.a,&b1=u.b,&b2=v.b;
return ((b2-a1) * (b1-a1)) * ((b1-a1) * (a2-a1))>=0&&((b1-a2) * (b2-a2)) * ((b2-a2) * (a1-a2))>=0;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(p[i].a.x),read(p[i].a.y),read(p[i].b.x),read(p[i].b.y);
t[(i<<1)-1]=p[i].a;
t[i<<1]=p[i].b;
p[i].a.id=p[i].b.id=p[i].id=t[(i<<1)-1].id=t[i<<1].id=i;
}
sort(t+1,t+1+(n<<1));
for(int i=1;i<=n<<1;i++){
set<node>::iterator it;
LL k=t[i].id;
ans1=k;
x=t[i].x;
it=s.find(p[k]);
if(it!=s.end()){
set<node>::iterator a_it=it,b_it=it;b_it++;
if(a_it!=s.begin()&&b_it!=s.end()){
a_it--;
if(cmp(p[a_it->id],p[b_it->id])){
ans1=a_it->id,ans2=b_it->id;
break;
}
}
s.erase(it);
}else{
set<node>::iterator new_it=s.lower_bound(p[k]);
if(new_it!=s.end()&&cmp(p[new_it->id],p[k])){
ans2=new_it->id;
break;
}
if(new_it!=s.begin()){
new_it--;
if(cmp(p[new_it->id],p[k])){
ans2=new_it->id;
break;
}
}
s.insert(p[k]);
}
}
LL ans=0;
if(ans1>ans2)
swap(ans1,ans2);
for(int i=1;i<=n;i++)
if(p[i].id!=ans2&&cmp(p[i],p[ans2]))
ans++;
printf("%lld",ans>1?ans2:ans1);
return 0;
}
3. 三角形(triangle.c/cpp/pas)
【题目描述】
平面上有n行m列,一共n*m个方格,从上到下依次标记为第1,2,...,n行,从左到右依次标记为第1,2,...,m列,方便起见,我们称第i行第j列的方格为(i,j)。小Q在方格中填满了数字,每个格子中都恰好有一个整数a_{i,j}。小Q不
喜欢手算,因此每当他不想计算时,他就会让你帮忙计算。小Q一共会给出q个询问,每次给定一个方格(x,y)和一个整数k(1<=k<=min(x,y)),你需要回答由(x,y),(x-k+1,y),(x,y-k+1)三个格子构成的三角形边上以及内部的所有
格子的a的和。
【输入格式】
第一行包含6个正整数n,m,q,A,B,C(1<=n,m<=3000,1<=q<=3000000,1<=A,B,C<=1000000)
其中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;
}
}
【输出格式】
为了防止输出数据过大,设f_i表示第i个询问的答案,则你需要输出一行一个整数,即:
i=1q233q-i*fi mod 232
(sum_{i=1}^q 233^{q-i}*f_i) mod 2^{32}
输入输出样例:
triangle.in | triangle.out |
3 4 5 2 3 7
| 3350931807 |
看着很多公式,但其实和简单,前缀和即可,分为矩形和梯矩形相减即可得出三角形,复杂度O(q)。
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL int
#define M 3005
void read(LL &x){
x=0;LL f=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-f;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
x*=f;
return ;
}
int n,m,q,x,y,k,x1,y1;
unsigned int A,B,C,a[M][M],b[M][M],c[M][M],d[3000005];
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(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
a[i][j] = rng61();
b[i][j]=b[i][j-1]+a[i][j];
c[i][j]=c[i-1][j+1]+b[i][j];
a[i][j]=b[i][j]+a[i-1][j];
}
c[i][0]=c[i-1][1];
}
d[0]=1;
for(int i=1;i<=3000000;i++)
d[i]=d[i-1]*233;
unsigned int ans=0;
for(int i = 1; i <= q; i++){
x = rng61() % n + 1;
y = rng61() % m + 1;
k = rng61() % min(x, y) + 1;
x1=x-k;
y1=y-k;
if(x1<0)
x1=0;
if(y1<0)
y1=0;
int s=a[x][y]-a[x1][y];
s=s-c[x][y1]+c[x1][y];
ans+=s*d[q-i];
}
printf("%u",ans);
}