一:赛时分数
T1【小可的动态数字锁(dynamic)】(60/100)
T2【无可奈何花落去(flower)】(20/100)
T3【小可的数字锁(lock)】(10/100)
T4【翻转同余(equiv)】(0/100)
总分(90/400)炸了(
二、赛事思路
T1(dynamic):
第一眼向量有点被唬住了,但浅浅分析一会(不到半小时)之后发现这个矩阵除了主对角线元素上计算的结果,其他结果都被计算了两遍,故非主对角线上的答案只能是2或0,而答案又要求对总答案%2,于是就想到只计算主对角线上1的个数,操作指令为1或2的时候改变该行/列上主对角线上的元素,操作指令为3的时候就输出就好了。
T2(flower):
看到题之后,首先想到brute force算法,否掉之后想到区间dp,后来否掉了。(不会写)考虑到部分分比较好拿,遂考虑的状态不全。(能得20也是情理之中)
T3(lock):
没啥思路,不会骗分,遂10分。(还挺不错)
T4(equiv):
数论一到编程就炸掉,没思路。但数据范围上提到了a奇偶性,尝试往那上面考虑,但是仍然想不出来啥,就放弃了,故0分。
三、题目正解:
T1(dynamic):
题目重现:
小可十分喜欢线性代数这门课。行列式、矩阵、向量...都让小可沉迷不已。
有一天,老师给了小可一个动态变化的矩阵。这个矩阵所有的元素都是0或者1。
老师告诉小可,这个动态矩阵用于一个重要部门的加密。这个矩阵通过特殊的运算能计算出一个分数。在矩阵的变化过程中计算多次分数形成一个01串,就是这个时刻的动态密码。由于矩阵和变化都是动态的,密码也是动态变化的。老师让小可写一个解密程序。你能帮助小可吗?
矩阵分数的计算方法:
给定一个的矩阵,这个矩阵的分数就是第一行与第一列的向量乘积+第二行与第二列的向量乘积+...+第n行与第n列的向量乘积模2的结果。
假设有一个向量,向量
,这两个向量的乘积结果为
如下图,一个的矩阵:

这个矩阵的分数为:
正解思路:
观察数据范围,发现暴力时间复杂度太高,就可以找规律。
可以发现,根据题目中运算的定义,相乘的两个位置一定是沿着主对角线对称的。假设是的矩阵,坐标
的对称坐标是
。故只有主对角线的元素会影响最终结果,所以我们可以
地维护主对角线上元素的变化。(和赛时思路大差不差)
AC code:
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005];
int main(){
int n,cnt=0;
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j&&a[i][j]==1) cnt++;
}
}
int q;
cin>>q;
while(q--){
int opt;
cin>>opt;
if(opt!=3){
int x;
cin>>x;
if(a[x][x]==0) cnt++,a[x][x]=1;
else cnt--,a[x][x]=0;
}else{
cout<<cnt%2;
}
}
return 0;
}
T2(flower):
题目重现:
再美丽的花,终究还是会凋谢。
小明养了四盆花,经历了绚烂的盛开,随着秋风涌起,花瓣渐落。
见证了花开的绚烂,看着花瓣日渐减少,小明逐渐意识到,残缺也是一种美。他打算等花掉落了一些花瓣后,将这四朵花做成标本,永久保留下来。
小明想让第一朵花掉落瓣花瓣,让第二朵花掉落
瓣花瓣,让第三朵花掉落
瓣花瓣,让第四朵花掉落
瓣花瓣。如此,这四朵花放一起,就会有一种残缺的不对称的美。
既然要追求不对称的残缺的美,那么,
,
,
。现在小明想知道,
这个四元组一共有多少?
正解思路:
这题主打一个容斥原理,考虑以下五种情况:
1、总情况
总情况就随便选,所以答案是
2、两两相交的情况
可以发现,这种情况在总情况中被重复计算了两次,所以需要减去
也就是的值分别为
这四种情况。注意:原题并没有说
不同等其他情况,但不等关系不存在传递性。
3、三三相交的情况
虽然不等关系不存在传递性,但是显然三三相交的情况一定不合法,因为总共就四个区间。
假设这三个相同,那么在总情况被算了一次,然后两两相交的时候,
和
总共又减掉了两次,所以需要再加上一次(因为这是非法情况,应该被算0次)。
假设三个区间相同,那么方案为
4、两对相等,但是对之间不相等
例如,
这种。这种情况在总情况被算一次,在两两相交的时候被减掉两次,所以要加回来一次。
5、四个全部相等
这种情况下在⼀开始被算了⼀次,然后两两相交的时候减掉四次,三三相等加回 来四次,两对相等加了两次,总共被加了三次,所以要减掉三次。 分类讨论整理即可。不过要注意的是,有些区间不存在,这种区间不要进⾏计算。
AC code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
int t;
ll l1,l2,l3,l4,r1,r2,r3,r4;
ll two(ll a,ll b,ll c,ll d){
return max(0ll,(min(b,d)-max(a,c)+1)%mod);
}
ll thr(ll a,ll b,ll c,ll d,ll e,ll f){
return max(0ll,(min(b,min(d,f))-max(a,max(c,e))+1)%mod);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&l3,&r3,&l4,&r4);
ll a1=(r1-l1+1)%mod,a2=(r2-l2+1)%mod,a3=(r3-l3+1)%mod,a4=(r4-l4+1)%mod;
ll all=a1%mod*a2%mod*a3%mod*a4%mod;
ll f1=(((two(l1,r1,l2,r2)*a3%mod*a4%mod//1==2
+two(l2,r2,l3,r3)*a4%mod*a1%mod)%mod//2==3
+two(l3,r3,l4,r4)*a2%mod*a1%mod)%mod//3==4
+two(l4,r4,l1,r1)*a2%mod*a3%mod)%mod;//4==1
ll f2=(((thr(l1,r1,l2,r2,l3,r3)*a4%mod//1==2==3√
+thr(l2,r2,l3,r3,l4,r4)*a1%mod)%mod//2==3==4√
+thr(l3,r3,l4,r4,l1,r1)*a2%mod)%mod//1==3==4√
+thr(l4,r4,l1,r1,l2,r2)*a3%mod)%mod;//1==2==4√
ll kf2=(two(l1,r1,l2,r2)*two(l3,r3,l4,r4)%mod//1==2&&3=4√
+two(l1,r1,l4,r4)*two(l2,r2,l3,r3)%mod)%mod;//1==4&&2==3√
ll f4=max(0ll,(min(r1,min(r2,min(r3,r4)))-max(l1,max(l2,max(l3,l4)))+1)%mod);//1
printf("%lld\n",((all-f1+f2+kf2-f4*3)%mod+mod)%mod);
}
return 0;
}
T3(lock):
题目重现:
小可设计了一个数字锁,谁要是成功解开了这个数字锁,就能获得小可准备好的礼物。
小可的数字锁的形状类似井字。如图所示。一共24个格子。每个格子都填了一个数字(只有可能是1、2、3中的一个)。如果图中黄色部分的所有格子的数字都相同,这个锁就被解开了。

我们能对这个锁做的操作有8种。我们看到,图中一共有4段由格子构成的线段。在他们的两个端点都分别有一个带圈的数字。我们按一个带圈的数字,那么这个线段上所有格子的数字都会向这个方向前进一位。原本在最首位的数字会到尾部。
比如:图中的状态,我们按带圈的数字1,相对应的线段:
原本的状态(从上到下看):
2 1 2 3 3 1 1
按下之后(从上到下看):
1 2 3 3 1 1 2
再比如,还是图中的状态,我们按带圈的数字6:
原本的状态(从上到下看):
2 1 2 3 3 1 1
按下之后(从上到下看):
1 2 1 2 3 3 1
请你输出一下如何最快解锁这个数字锁。
正解思路:
考虑使⽤ IDA* 。
我们知道迭代加深搜索,就是给 dfs 加上⼀个深度极限。如果在这个限定了的深度内搜索不到答案,那么 我们就把这个深度极限扩展⼀下,继续搜索。这样的话,第⼀次搜到结果就必然是最短(这个原理等同 于 bfs 第⼀次搜到就是最短)。这样想,迭代加深搜索就是⽤ dfs 去写 bfs 。
但是迭代加深搜索也有⼀个严重的问题,那就是重复性。我们会反反复复地搜索⽐较浅的那些层。这样 我们就需要加⼀些剪枝来加快搜索速度。在迭代加深搜索的基础上,我们设定⼀个估价函数 h() ⽤来描述 当前状态到⽬标状态的估计值,然后⽤ now+h()>depth 来剪枝。这就是 IDA* 。
那么这个题的估价函数怎么设定?我们想⼀下,每⼀步的最优状态,也就是在原有的状态上加⼀个新的 ⽬标数字。
⽐如: 假设原本⻩⾊区域有 5 个 1 , 1 个 2 , 2 个 3 。不管其他地⽅如何,我们最快到达解锁状态的⽅法就是把其他 的数字都转化成 1 。每⾛⼀步,最好的情况也就是把⼀个其他数字换成 1 。也就是说想要达到解锁状态最 少也还需要三步。那么如果这时候,距离搜索深度极限还有 2 步,那么这个搜索就没有必要继续下去 了。因为在限定深度内,即使是最理想的状态也⽆法⾛到解锁的状态。
那么我们的估价函数,就很容易写出来了。我们把⻩⾊部分所有数字的个数统计出来,挑选出⼀个出现 最多的次数,然后⽤ 8 去减,也就是最理想状态还需要⼏步。然后如果⽬前深度加上估价函数⼤于限定 深度,就直接退出。
当然还有⼀个需要注意的地⽅。⽐如我们第⼀步按了带圈数字 1 ,第⼆部按了带圈数字 6 。这两个操作是 相反的,相邻放在⼀起刚好抵消掉了,那么这就是做了⽆⽤功。所以我们还需要加⼀个 pre 来保存⼀下 上⼀步的状态是什么。那么我们之前按了带圈数字 1 ,那么之后都不能按带圈数字 6 了吗?当然不是。这 个问题意想便知,只是相反的操作不能相邻。
AC code :
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int pos[8][8]={{1,3,7,12,16,21,23},{2,4,9,13,18,22,24},{11,10,9,8,7,6,5},{20,19,18,17,16,15,14},{24,22,18,13,9,4,2},{23,21,16,12,7,3,1},{14,15,16,17,18,19,20},{5,6,7,8,9,10,11}};
int center[8]={7,8,9,12,13,16,17,18};
int preStatus[9]={5,4,7,6,1,0,3,2,-1};
int a[30],k,vis[4],flag;
char path[1000];
int evaluate(){
vis[1]=vis[2]=vis[3]=0;
for(int i=0;i<8;i++) vis[a[center[i]]]++;
return 8-max(vis[1],max(vis[2],vis[3]));
}
void move(int x){
int tmp=a[pos[x][0]];
for(int i=0;i<6;i++) a[pos[x][i]]=a[pos[x][i+1]];
a[pos[x][6]]=tmp;
}
void idastar(int depth,int pre){
if(flag) return ;
int eva=evaluate();
if(depth>k||depth+eva>k) return ;
if(!eva){
flag=1;
path[depth]='\0';
printf("%s\n%d\n",path,a[center[0]]);
return ;
}
for(int i=0;i<8;i++){
if(i==preStatus[pre]) continue;
move(i);
path[depth]=i+'1';
idastar(depth+1,i);
move(preStatus[i]);
}
}
int main(){
while(scanf("%d",&a[1]),a[1]){
flag=0;
for(int i=2;i<=24;i++) scanf("%d",&a[i]);
if(!evaluate()){
printf("It's unlocked!\n%d\n",a[center[0]]);
continue;
}
for(k=1;!flag;k++){
idastar(0,8);
}
}
return 0;
}
T4(equiv):
题目重现:
小可在学习同余方面的知识。突发奇想,小可想到了一个奇特的同余形式:
给定和
,小可想知道,有多少个在
中的
满足这个同余方程。
正解思路:
这个题可以通过打表找规律。
我们在看这个题的时候,应该会发现,数据范围中对的奇偶性进⾏了分类。其实这就是题⽬提⽰的从 奇偶性⻆度⼊⼿。
我们可以通过观察得到:
模数一定是偶数
和
的奇偶性一定相同 (同余的定义可证)
1、对奇数情况进行讨论
我们可以把一个奇数表示为。容易得到
所以能得到
显然,因为a是偶数,所以a的幂模2就是1,所以
同理
因为
所以
所以只有一种情况——
继续对2的幂进行讨论。奇数的平方可以表示成,所以显然有
所以
同理,又有
对于8来说,我们观察,这里
显然一定能整除以2。所以有
所以
同理,又有
可以发现之前都能证明,对于更大的
,尝试证明。
——引入一个定理:
,若有
,则有
既然,所以也有
同理,有:以及
所以
可以发现,对于奇数的情况,只有一种情况。所以输入技术,直接输出1即可。这个规律我们也可以通过暴力打表发现。
2、对偶数情况进行讨论
我们可以把a中的2拆出来,就可以得到
当b大于n时,显然,所以也能得到结论
我们把b表示成
所以
显然有
也就是说
所以所有的形式的b都可以。可以知道,x最小是
。
这样的b有多少个呢?注意,我们当前的前提是,所以
的情况不能算上。也就是说,在这个范围内,
的倍数都符合条件。也就是
那么的情况怎么办呢?数据范围非常小,暴力验证即可。
AC code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll qpow(ll a,ll b,ll mod){
ll ans=1ll;
while(b){
if(b&1)ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans%mod;
}
int main(){
ll a,n;
while(~scanf("%lld%lld",&n,&a)){
if(a&1){
printf("1\n");
continue;
}
ll m=1<<n;
ll ans=0ll;
for(ll b=2;b<=n;b+=2)
if(qpow(a,b,m)==qpow(b,a,m)) ans++;
ll x=(n+a-1)/a;
ans+=(m>>x)-(n>>x);
printf("%lld\n",ans);
}
return 0;
}
四、赛后总结
以后可以适当做一点有思维含量的题,就不会出现数论题爆0的情况。

3132

被折叠的 条评论
为什么被折叠?



