Week 1
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\) 个方格。每个方格内有一个小灯泡,灯泡的状态有两种(亮或暗)。我们可以通过若干操作使魔板从一个状态改变为另一个状态。操作的方式有两种:
- 任选一行,改变该行中所有灯泡的状态,即亮的变暗、暗的变亮;
- 任选两列,交换其位置。
当然并不是任意的两种状态都可以通过若干操作来实现互相转化的。
你的任务就是根据给定两个魔板状态,判断两个状态能否互相转化。
输入格式
文件中包含多组数据。第一行一个整数 \(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;
}
//**月雩·薇嫭**