2019/8/15题解+总结

本次总结回顾了算法竞赛中的得失,分析了时间管理、贪心策略及基础知识的应用,探讨了USACO19OPEN LeftOut、Cow Steeplechase II等题目的解题思路,分享了代码实现细节,强调了前缀和、线段树等数据结构的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今日总结:

爆零了,主要原因在于时间分配不匀,和太过贪心,以及基础知识反应不及时。

第一题我没想出正解,但是-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个也不行,所以求出相连两条即可退出。

首先想到n^{2}复杂度做法,但明显超时,需要优化,所以采取扫描线进行优化至nlog(n)

现将每个点(注意,分开了,但要标记属于那条线)按照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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值