ZROI CSP七连测 Day 1
这篇其实早就写好了,只是一直搞忘了放上来。
A
给定一个 行 列的 网格图,计算有多少个 行 列的子矩形满足 存在大于等于 对相邻且不同色的格子。
二维前缀和即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using namespace std;
typedef long long ll;
const int maxn=2005;
int n,m,h,w,K;
int g[maxn][maxn];
int f[maxn][maxn],row[maxn][maxn],line[maxn][maxn];
int ans;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m>>h>>w>>K;
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
for(int j=0;j<m;j++) g[i][j+1]=s[j]-'0';
}
for(int i=1;i<=n;i++)
{
for(int j=2;j<=m;j++)
{
row[i][j]=row[i][j-1];
if(g[i][j]!=g[i][j-1]) row[i][j]++;
}
}
for(int j=1;j<=m;j++)
{
for(int i=2;i<=n;i++)
{
line[i][j]=line[i-1][j];
if(g[i][j]!=g[i-1][j]) line[i][j]++;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
line[i][j]+=line[i][j-1];
row[i][j]+=row[i-1][j];
}
}
for(int i=1;i<=n-h+1;i++)
{
for(int j=1;j<=m-w+1;j++)
{
int num=row[i+h-1][j+w-1]-row[i+h-1][j]-row[i-1][j+w-1]+row[i-1][j];
num+=line[i+h-1][j+w-1]-line[i][j+w-1]-line[i+h-1][j-1]+line[i][j-1];
if(num>=K) ++ans;
}
}
cout<<ans<<endl;
return 0;
}
B
给定一个有 个整数的环,每次可以选择相邻的两个并把它们减少 ,问是否存在把所有数都变成 的方案
设 为同时在 和 进行操作的次数,那么就可以得到 条限制,每条限制形如 。设 (最后的正负由 的奇偶性决定)。
我们可以通过上述 条限制把所有 用 表示出来,那么可以得到:
把这个式子代入到 中来,那么若 是奇数,就可以把 解出来,进而解出所有的 ,若满足所有 ,即为合法;若 是偶数,可以发现,当且仅当 时,才存在合法的方案。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using namespace std;
typedef long long ll;
int t;
const int maxn=5e5+5;
int n;
int a[maxn];
int main()
{
//freopen("ex_B2.in","r",stdin);
//freopen("b.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
ll sum=0;
for(int i=1;i<=n;i++)
{
if(i&1) sum+=a[i];
else sum-=a[i];
}
if(n&1)
{
if(sum&1)
{
cout<<0<<endl;
continue;
}
bool flag=true;
for(int i=1;i<=n;i++)
{
sum=2*a[i]-sum;
if(sum<0)
{
flag=false;
break;
}
}
if(sum>=0&&flag) cout<<1<<endl;
else cout<<0<<endl;
}
else
{
if(!sum) cout<<1<<endl;
else cout<<0<<endl;
}
}
return 0;
}
C
给定一条直线上的 个位置,从左到右编号为 到 ,第 个位置里有 个不同的物品。从 出发,在每一个时刻都会离开当前所在位置去往下一个位置,若当前位置为 ,到达的下一个位置 需满足 ,其中 为只包含不大于 的正整数的集合,若 ,离开的同时还会破坏第 个位置中的之多一个物品。
给定一个正整数 ,若最后到达了 号位置,那么你有多少种破坏物品的方案,答案对 取模。
可以写成矩阵乘法的形式,然后发现转移矩阵存在长为 的循环节,因此可以先暴力计算一个循环节相乘的结果,再对这个结果做矩阵快速幂,最后乘上不满一个循环节的部分。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using namespace std;
typedef long long ll;
const int maxs=16;
const int mod=2027;
struct Matrix
{
int a[maxs][maxs];
Matrix() {memset(a,0,sizeof(a));}
Matrix operator * (const Matrix& x) const
{
Matrix res;
for(int i=1;i<=15;i++)
{
for(int j=1;j<=15;j++)
{
for(int k=1;k<=15;k++)
{
res.a[i][j]=(res.a[i][j]+1ll*a[i][k]*x.a[k][j])%mod;
}
}
}
return res;
}
};
Matrix qpow(Matrix a,ll n)
{
Matrix res;
if(n==0)
{
for(int i=1;i<=15;i++) res.a[i][i]=1;
return res;
}
res=a;
--n;
while(n)
{
if(n&1) res=res*a;
a=a*a;
n>>=1;
}
return res;
}
ll n,m;
int s[maxs];
int main()
{
//freopen("ex_C2.in","r",stdin);
//freopen("c.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>s[i];
ll a=n/mod,b=n%mod;
Matrix x,y;
for(int i=1;i<=15;i++) x.a[i][i]=1,y.a[i][i]=1;
for(int i=1;i<=mod;i++)
{
Matrix tmp;
for(int j=1;j<=m;j++) tmp.a[1][s[j]]=(i-s[j]+1+mod)%mod;
for(int j=2;j<=15;j++) tmp.a[j][j-1]=1;
x=tmp*x;
if(b==i) y=x;
}
Matrix ans=y*qpow(x,a);
cout<<ans.a[1][1]<<endl;
return 0;
}
D
给定长度为 的序列 ,要求每次对从 到 的 ,将 添加到序列 的开头或结尾。求 的最小值。
考虑二分答案 ,然后考虑每次放入一个新的数后,序列两侧可能放下的数的集合。
首先放下 和 后,两侧的集合分别是区间 和 ;
然后考虑放 ,如果它只被其中一个区间包含,那么它的放法是唯一的,而没放的那一边的区间不变;如果它被两个区间同时包含,那我们既可以把它放到左边,又可以放到右边,那么没放的那一边可能放下的数的集合即为 。而由于 被两个区间同时包含,一次这两个区间是有交的,因此这个并集仍然是一个区间。
以此类推,可以归纳证明,当我们放入一个数后,它另一侧能放的数的集合永远是一个区间。若下一个数只被其中一个区间包含,那么下一个数另一侧的区间就是它不能被放入的那个区间;如果被两个包含,另一侧的区间是两者的并;如果都不能包含,则无解。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int n;
int a[maxn];
inline bool check(int k)
{
int l=a[1]-k,r=a[1]+k;
int l1,r1;
for(int i=3;i<=n;i++)
{
l1=a[i-1]-k,r1=a[i-1]+k;
if(a[i]>=l1&&a[i]<=r1&&a[i]>=l&&a[i]<=r) l=min(l1,l),r=max(r1,r);
else if(a[i]>=l&&a[i]<=r) l=l1,r=r1;
else if(a[i]<l1||a[i]>r1) return 0;
}
return 1;
}
int main()
{
//freopen("d.in","r",stdin);
//freopen("d.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int l=abs(a[1]-a[2]),r=0;
for(int i=1;i<=n;i++) r=max(r,a[i]);
while(l<r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}