Week 1

发布时间 2023-10-15 22:21:32作者: 月雩·薇嫭

AC:
[NOIP1999 提高组] 邮票面值设计
P1032 [NOIP2002 提高组] 字串变换
B3614 【模板】栈
P3372 【模板】线段树 1
P2118 [NOIP2014 普及组] 比例简化
P1873 [COCI2011-2012#5] EKO / 砍树
P3374 【模板】树状数组 1
P3368 【模板】树状数组 2
P5367 【模板】康托展开
P6216 回文匹配
P5490 【模板】扫描线
P3865 【模板】ST 表
P5358 [SDOI2019] 快速查询
P3370 【模板】字符串哈希
P1275 魔板
P1824 进击的奶牛
[蓝桥杯 2019 省 B] 等差数列
P1226 【模板】快速幂
P2249 【深基13.例1】查找

【模板】扫描线

题目描述

\(n\) 个四边平行于坐标轴的矩形的面积并。

输入格式

第一行一个正整数 \(n\)

接下来 \(n\) 行每行四个非负整数 \(x_1, y_1, x_2, y_2\),表示一个矩形的四个端点坐标为 \((x_1, y_1),(x_1, y_2),(x_2, y_2),(x_2, y_1)\)

输出格式

一行一个正整数,表示 \(n\) 个矩形的并集覆盖的总面积。

样例 #1

样例输入 #1

2
100 100 200 200
150 150 250 255

样例输出 #1

18000

提示

对于 \(20\%\) 的数据,\(1 \le n \le 1000\)
对于 \(100\%\) 的数据,\(1 \le n \le {10}^5\)\(0 \le x_1 < x_2 \le {10}^9\)\(0 \le y_1 < y_2 \le {10}^9\)

题解

把所有x坐标从小到大排序后去重 e.g.

x1      x2    x3          x4
  tree4   tree5   tree6
       tree2        tree3
               tree1
(tree4=x1~x2,tree5=x3~x1,……) 
(tree.l不变,tree.r是r正常线段树r-1)
像这样建一棵线段树

把每个方块下底赋值 1,上底赋值 -1

从下往上扫,修改线段树上的值(可以避免重复计算)

加一个tree.now 表示这个区间包含了几个方块

若tree.now>=1, 那 直接计算 x[tree.r+1]-x[tree.l] 

否则则用子节点计算 

最后总和为 tree1 ,即为这个 高度上线段的长度

最后乘一下高度,相加就是答案 

看不懂代码看题解  https://ncc79601.blog.luogu.org/scan-line 

code:

#include<bits/stdc++.h>
#define prf printf
#define scf scanf
#define ll long long
using namespace std;
const int N=2e6+10,M=998244353;
ll n,P[N],ans;
int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct sx
{
	ll l,r,h,val;
} Line[N];
struct sy
{
	ll l,r,len,now;
} tree[N*2];
bool cmd(sx x,sx y){return x.h<y.h;}
void Build(int x,int l,int r)
{
	tree[x].l=l,tree[x].r=r;
	if(l==r)return;
	int mid=(l+r)>>1;
	Build(x*2,l,mid);
	Build(x*2+1,mid+1,r); 
}
void update(int x)
{
	int l=tree[x].l,r=tree[x].r;
	if(tree[x].now)
		tree[x].len=P[r+1]-P[l];
	else tree[x].len=tree[x*2].len+tree[x*2+1].len;
}
void Change(int x,ll lx,ll rx,int k)
{
	ll l=tree[x].l,r=tree[x].r;
	if(rx<=P[l]||P[r+1]<=lx)return;
	if(P[l]>=lx&&P[r+1]<=rx)
	{	
		tree[x].now+=k;	
		update(x);	
		return;	
	}
	Change(x*2,lx,rx,k);
	Change(x*2+1,lx,rx,k);	
	update(x);
}
int main()
{
	//freopen("testdata.in","r",stdin);
	//freopen("get.out","w",stdout);	
	n=read();	
	for(int i=1;i<=n;i++)
	{		
		ll x_1=read(),y_1=read(),x_2=read(),y_2=read();		
		if(x_1>x_2)swap(x_1,x_2);	
		if(y_1>y_2)swap(y_1,y_2);
		P[i*2-1]=x_1,P[i*2]=x_2;
		Line[i*2-1]=(sx){x_1,x_2,y_1,1};	
		Line[i*2]=(sx){x_1,x_2,y_2,-1};
	}
	n*=2;sort(P+1,P+n+1);int tot=0;
	sort(Line+1,Line+n+1,cmd);
	tot=unique(P+1,P+n+1)-P-1;//使用unique可快速去重!!! 
	Build(1,1,tot-1);
	for(int i=1;i<n;i++)
	{
		Change(1,Line[i].l,Line[i].r,Line[i].val);	
		ans+=(Line[i+1].h-Line[i].h)*tree[1].len;	
	}
	cout<<ans;
	return 0;	
} 

魔板

题目描述

有这样一种魔板:它是一个长方形的面板,被划分成 \(n\)\(m\) 列的 \(n \times m\) 个方格。每个方格内有一个小灯泡,灯泡的状态有两种(亮或暗)。我们可以通过若干操作使魔板从一个状态改变为另一个状态。操作的方式有两种:

  1. 任选一行,改变该行中所有灯泡的状态,即亮的变暗、暗的变亮;
  2. 任选两列,交换其位置。

当然并不是任意的两种状态都可以通过若干操作来实现互相转化的。

你的任务就是根据给定两个魔板状态,判断两个状态能否互相转化。

输入格式

文件中包含多组数据。第一行一个整数 \(k\),表示有 \(k\) 组数据。

每组数据的第一行两个整数 \(n\)\(m\)\(0 < n,m \leq 100\))。

以下的 \(n\) 行描述第一个魔板。每行有 \(m\) 个数字(\(0\)\(1\)),中间用空格分隔。若第 \(x\) 行的第 \(y\)个数字为 \(0\),则表示魔板的第 \(x\)\(y\) 列的灯泡为“亮”;否则为“暗”。

然后的 \(n\) 行描述第二个魔板。数据格式同上。

任意两组数据间没有空行。

输出格式

\(k\) 行,依次描述每一组数据的结果。

若两个魔板可以相互转化,则输出 \(\texttt{YES}\),否则输出 \(\texttt{NO}\)。(注意:请使用大写字母)

样例 #1

样例输入 #1

2
3 4
0 1 0 1
1 0 0 1
0 0 0 0
0 1 0 1
1 1 0 0
0 0 0 0
2 2
0 0
0 1
1 1
1 1

样例输出 #1

YES
NO

题解:

1.每一行最多变换一次,否则没有意义
2.魔板A中第\(i\)列1的个数和0的个数必定和B中相等,若不等则比不可能性转化
3.特殊情况是第\(i\)行1和0数量相等,此时不能判断这一行是否需要变换状态
4.B中第x列,枚举A的列,当这一列没被匹配过且所有数字都和B的第x列相等或者当某几个数字不等,但那一列可以转换状态(之前未转换过)使之相等,这一列可以与x列匹配

Code:

#include<bits/stdc++.h>
#define FQ(i,a,b) for(register int i=a;i<=b;i++)
#define prf printf
#define scf scanf
#define ll long long
using namespace std;
const int N=1e3+20;
int a[N][N],b[N][N],n,m,pd,ans,num[N][N],vis[N],tot,visx[N];
ll read()
{
	
	ll x=0,f=1;char ch=getchar();
	
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	
	return x*f;
	
}

void change(int x)
{
	
	for(int i=1;i<=m;i++)a[x][i]=(-a[x][i]+1);
	
}

void Find(int x)
{
	
	if(pd)return;
	
	if(x==m+1)
	{
		
		pd=true;
		
		return;
		
	}
	
	int visy[111];
	
	for(int i=1;i<=n;i++)visy[i]=visx[i];
	
	for(int i=1;i<=m;i++)
	{
		
		if(vis[i])continue;
		
		int numx=0;
		
		for(int j=1;j<=n;j++)
		{
			
			if(a[j][i]==b[j][x])numx++;
			
			else if(visx[j])
			{
				
				change(j);
				
				visx[j]=0;
				
				numx++;
				
				
			}
			
		}
			
		if(numx==n)
		{
			
			vis[i]=true;
			
			Find(x+1);
			
			vis[i]=false;
			
		}
		
		for(int j=1;j<=n;j++)
		{
			
			if(visx[j]+visy[j]==1)change(j);
			
			visx[j]=visy[j];
		}
		
	}

}

int main()
{
	
	//freopen(".in","r",stdin);
	
	//freopen(".out","w",stdout);
	
	int T=read();
	
	while(T--)
	{
		
		n=read(),m=read();pd=0;tot=0;
		
		memset(num,0,sizeof(num));
		
		memset(vis,0,sizeof(vis));
		
		memset(visx,0,sizeof(visx));
		
		for(int i=1;i<=n;i++)
		
			for(int j=1;j<=m;j++)
			{
				
				a[i][j]=read();
				
				num[i][a[i][j]]++;
				
			}
				
		for(int i=1;i<=n;i++)
		{
			
			int l=0;
			
			for(int j=1;j<=m;j++)
			{
				
				b[i][j]=read();
				
				if(b[i][j])l++;
				
			}
			
			if(l==num[i][1]&&l*2!=m)continue;
			
			if(m-l==num[i][1]&&l*2!=m)
				for(int j=1;j<=m;j++)
					a[i][j]=(-a[i][j]+1);
					
			if(m-l==num[i][1]&&l*2==m)visx[i]=true;
				
		}
		
		Find(1);	
		
		if(!pd)cout<<"NO\n";
		
		else cout<<"YES\n";
		
	}	
	
	
	
	
	
	return 0;	
	
} 

[蓝桥杯 2019 省 B] 等差数列

题目描述

数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一部分的数列,只记得其中 \(N\) 个整数。

现在给出这 \(N\) 个整数,小明想知道包含这 \(N\) 个整数的最短的等差数列有几项?

输入格式

输入的第一行包含一个整数 \(N\)

第二行包含 \(N\) 个整数 \(A_1,A_2,\cdots,A_N\)。(注意 \(A_1 ∼ A_N\) 并不一定是按等差数列中的顺序给出 )。

输出格式

输出一个整数表示答案。

样例 #1

样例输入 #1

5
2 6 4 10 20

样例输出 #1

10

提示

包含 2,6,4,10,20 的最短的等差数列是 2,4,6,8,10,12,14,16,18,20

对于所有评测用例,\(2 \le N \le 10^5\)\(0 \le A_i \le 10^9\)

蓝桥杯 2019 年省赛 B 组 H 题。

题解:

将数组排序,找所有的任意两个数差的绝对值的最大公因数
特别注意当所有数相同时,所有的差都为零,此时公差为0,不能求gcd

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10;
int a[N],n,d;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int main ()
{
	
	n=read();
	
	for(int i=1;i<=n;i++)a[i]=read();
	
	sort(a+1,a+n+1);
	
	if(a[n]==a[1])
	{
		cout<<n;exit(0);
	}
	
	int d=a[2]-a[1];
	
	for(int i=2;i<n;i++)
	{
		d=__gcd(d,a[i+1]-a[i]);
	}
				
	printf("%d",(a[n]-a[1])/d+1);

	return 0;
}

【模板】快速幂

题目描述

给你三个整数 \(a,b,p\),求 \(a^b \bmod p\)

输入格式

输入只有一行三个整数,分别代表 \(a,b,p\)

输出格式

输出一行一个字符串 a^b mod p=s,其中 \(a,b,p\) 分别为题目给定的值, \(s\) 为运算结果。

样例 #1

样例输入 #1

2 10 9

样例输出 #1

2^10 mod 9=7

提示

样例解释

\(2^{10} = 1024\)\(1024 \bmod 9 = 7\)

数据规模与约定

对于 \(100\%\) 的数据,保证 \(0\le a,b < 2^{31}\)\(a+b>0\)\(2 \leq p \lt 2^{31}\)

题解:

e.g.
\(a^5\)=\(a^4+a^1\)
可以发现\(5\)的二进制为101,从右到左第一位是1,所以需要乘上\(a^2\),第三位是1,所以需要乘上\(a^(2^2)\)
由此可以得出:当\((b>>t)&1==1\)\(ans\)就要乘上\(a\)\(t\)次方,时间复杂度O(logb)

Code:

#include<bits/stdc++.h>
#define prf printf
#define scf scanf
#define ll long long
using namespace std;
ll M=1e9+7;
int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll ksm(ll a,ll b)
{
	ll ans=1;a%=M;//注意,这里一定要模一次! 
	while(b)
	{
		if(b&1)ans=(ans*a)%M;
		a=(a*a)%M;
		b>>=1;
	}
	return ans;
}
int main()
{
	//freopen("testdata.in","r",stdin);
	//freopen("get.out","w",stdout);
	ll n=read(),m=read();
	printf("%lld",ksm(n,m));
	return 0;	
} 
//**月雩·薇嫭**

【深基13.例1】查找

题目描述

输入 \(n\) 个不超过 \(10^9\) 的单调不减的(就是后面的数字不小于前面的数字)非负整数 \(a_1,a_2,\dots,a_{n}\),然后进行 \(m\) 次询问。对于每次询问,给出一个整数 \(q\),要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 \(-1\)

输入格式

第一行 \(2\) 个整数 \(n\)\(m\),表示数字个数和询问次数。

第二行 \(n\) 个整数,表示这些待查询的数字。

第三行 \(m\) 个整数,表示询问这些数字的编号,从 \(1\) 开始编号。

输出格式

输出一行,\(m\) 个整数,以空格隔开,表示答案。

样例 #1

样例输入 #1

11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6

样例输出 #1

1 2 -1

提示

数据保证,\(1 \leq n \leq 10^6\)\(0 \leq a_i,q \leq 10^9\)\(1 \leq m \leq 10^5\)

本题输入输出量较大,请使用较快的 IO 方式。

题解:

二分查找第一个满足要求的数
注意当二分时,若有l=mid,则mid的赋值要改为(l+r)>>1,否则当r==l+1时会出现死循环

Code:

#include<bits/stdc++.h>
#define FQ(i,a,b) for(register int i=a;i<=b;i++)
#define prf printf
#define scf scanf
#define ll long long
using namespace std;
int INF;
const int N=1e6+100;
void FindMaxN()
{

	int fINDmAXn[1];
	
	memset(fINDmAXn,128/2,sizeof(fINDmAXn));
	
	INF=fINDmAXn[0];
	
}

ll read()
{
	
	ll x=0,f=1;char ch=getchar();
	
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	
	return x*f;
	
}
ll a[N];int n,m;
int main()
{
	
	//freopen(".in","r",stdin);
	
	//freopen(".out","w",stdout);
	
	FindMaxN();
	
	n=read(),m=read();
	
	for(int i=1;i<=n;i++)a[i]=read();
	
	for(int i=1;i<=m;i++)
	{
		
		ll x=read();
		
		int L=1,R=n,ans=0;
		
		while(L<=R)
		{
			
			int Mid=(L+R)>>1;
			
			if(a[Mid]>=x)
			{
			
				R=Mid-1;
				
				if(a[Mid]==x)ans=Mid;
				
			}
			
			else L=Mid+1;
			
		}
		
		printf("%d ",ans?ans:-1);
		
	}
	
	return 0;	
	
} 
//**月雩·薇嫭**

进击的奶牛

题目描述

Farmer John 建造了一个有 \(N\)\(2 \leq N \leq 10 ^ 5\)) 个隔间的牛棚,这些隔间分布在一条直线上,坐标是 \(x _ 1, x _ 2, \cdots, x _ N\)\(0 \leq x _ i \leq 10 ^ 9\))。

他的 \(C\)\(2 \leq C \leq N\))头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John 想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?

输入格式

\(1\) 行:两个用空格隔开的数字 \(N\)\(C\)

\(2 \sim N+1\) 行:每行一个整数,表示每个隔间的坐标。

输出格式

输出只有一行,即相邻两头牛最大的最近距离。

样例 #1

样例输入 #1

5 3
1
2
8
4
9

样例输出 #1

3

题解:

二分查找答案,每查找一个答案\(k\),check是否满足题意
即第一个奶牛住进1号,第二个住进第i号,x满足x[i]-x[1]>=k,且i最小,以此类推

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
int n,c,x[N];
inline ll read()
{
	
	ll x=0,f=1;char ch=getchar();
	
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	
	return x*f;
	
}

bool check(int k)
{
	
	int num=1,t=1;
	
	for(int i=2;i<=n;i++)
		if(x[i]-x[t]>=k)
			t=i,num++;
	
	if(num>=c)return true;
	
	return false; 
	
}

int main()
{
	
	//freopen(".in","r",stdin);
	
	//freopen(".out","w",stdout);
	
	n=read(),c=read();
	
	for(int i=1;i<=n;i++)x[i]=read();
	
	sort(x+1,x+n+1);
	
	int l=1,r=x[n]-x[1];
	
	while(l<r)
	{
		
		int mid=(l+r+1)>>1;
		
		if(check(mid))l=mid;
		
		else r=mid-1;
		
	}
	
	cout<<l;
	
	return 0;	
	
} 
//**月雩·薇嫭**