一篇来自ACM入门者的补题记录
赶着在周五晚上爬上来更新这篇博客,因为明天就是天梯赛了,本来计划是在天梯赛前补完秦皇岛的题的(捂脸…)计划赶不上变化啊…
这次更新的周期也比较久,因为可补的题目比较多,还是有两题没能补上…
天梯赛加油~
题意:题意不是很清楚,大体上是n个点,n-1条边的图,1号点是出口,在某些点上有人,所有人一起行动,但同一个点上只能同时存在一个人,问让所有人逃离迷宫的最短时间是多少。
思路:设 t t t 为我们当前经过的时间,d为某一个人距离1号点的距离,当最后一个人逃出迷宫时的 t t t 为我们的答案,首先用DFS预处理出所有人距离1号点的距离。
距离1号点的最近的人a没有任何干扰,直接逃离迷宫,此时 t = d ( a ) t=d(a) t=d(a)。
对于下一个人离1号点最近的b而言,此时必然存在 d ( b ) > = t d(b)>=t d(b)>=t,否则b就是先逃离迷宫的人了:
若 d ( b ) = t d(b)=t d(b)=t,说明b在某一个点肯定和上一个人a碰上了,本来b花费 t t t 时间也能到达1号点了,但因为和上一个人碰上,就得等待一个时刻,因为题意要求同一个点不能同时存在两个人,此时更新 t + + t++ t++。
若 d ( b ) > t d(b)>t d(b)>t,说明上一个人逃离一段时间后,b才赶来,两人不会起冲突,此时总体花费的时间就是 d ( b d(b d(b)了,更新 t = d ( b ) t=d(b) t=d(b),重复上述过程,直到最后一个人离开。
#include
using namespace std;
const int maxn = 1e5+5;
int vis[maxn],d[maxn];
vectorG[maxn];
int num = 0;
void DFS(int fa,int x,int depth){
if(vis[x])
d[num++] = depth;
for(int u : G[x]){
if(u == fa)
continue;
DFS(x,u,depth+1);
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i<=n;i++)
scanf("%d",&vis[i]);
for(int i = 1;i
题意:定义 F i b n Fib_n Fibn为斐波那契数列,现在给定一个整数 R R R,求 ∑ n = 1 R F i b n & F i b n − 1 \sum_{n=1}^{R}{Fib_n\&Fib_{n-1}} ∑n=1RFibn&Fibn−1,答案对998244353。
思路:补起来稍微有点吃力的题目。
首先我们把 F i b n & F i b n − 1 Fib_n\&Fib_{n-1} Fibn&Fibn−1分解开来,发现 F i b n & F i b n − 1 = F i b n − l o w b i t ( F i b n ) Fib_n\&Fib_{n-1} = Fib_n-lowbit(Fib_n) Fibn&Fibn−1=Fibn−lowbit(Fibn),
题目转变为求 ∑ n = 1 R F i b n − ∑ n = 1 R l o w b i t ( F i b n ) \sum_{n=1}^{R}{Fib_n}-\sum_{n=1}^{R}{lowbit(Fib_n)} ∑n=1RFibn−∑n=1Rlowbit(Fibn)
前者,网上有等比数列求和公式的求解过程, ∑ n = 1 R F i b n = F i b n + 2 − 1 \sum_{n=1}^{R}{Fib_n}=Fib_{n+2}-1 ∑n=1RFibn=Fibn+2−1,单个斐波那契数的值则可以用矩阵快速幂解决,参见 poj3070。
后者,比较没有头绪,可以先打个表。发现 l o w b i t ( F i b n ) lowbit(Fib_n) lowbit(Fibn)是这样一个序列:
1   1   2   1   1   8   1   1   2   1   1   16   1 ⋯ 1\,1\,2\,1\,1\,8\,1\,1\,2\,1\,1\,16\,1\cdots 11211811211161⋯,把 1   1   2   1   1 1\,1\,2\,1\,1 11211这个以6为单位循环出现的子序列去掉,剩下 8   16   8   32   8   16   8   64 ⋯ 8\,16\,8\,32\,8\,16\,8\,64\cdots 816832816864⋯提取公因数 8 8 8,剩下序列为 1   2   1   4   1   2   1   8   1   2   1   4   1   2   1   16   1   2 ⋯ 1\,2\,1\,4\,1\,2\,1\, 8\,1\,2\,1\,4\,1\,2\,1\,16\,1\,2\cdots 1214121812141211612⋯
假设这个特殊序列的长度为n,这个序列的特点就是:
1的个数为 n / 2 + ( n & ( 1 ) ) n/2 + (n\&(1)) n/2+(n&(1)),2的个数为 n / 4 + ( n & ( 2 ) ) n/4 + (n\&(2)) n/4+(n&(2)),4的个数为 n / 8 + ( n & ( 4 ) ) n/8 + (n\&(4)) n/8+(n&(4)) ⋯ \cdots ⋯ 2 k 2^k 2k的个数为 n / 2 k + 1 + ( n & ( 2 k ) ) n/2^{k+1}+(n\&(2^k)) n/2k+1+(n&(2k))
然后求和,扣一扣余数细节,就大功告成了。这是我第一次写矩阵快速幂,顺手把 poj3070解决了,打表规律想得比较久…
#include
using namespace std;
const int mod = 998244353;
#define ll long long
ll c[2][2];
void multi(ll a[2][2],ll b[2][2],int n){
memset(c,0,sizeof c);
for(int i = 0;i>=1;
}
}
ll solve(ll x){
ll sum = 0;
ll k = 1;
while(k <= x){
if(x & k)
sum = (sum+(x/(2*k)+1)*k)%mod;
else
sum = (sum+x/(2*k)*k)%mod;
k*=2;
}
return sum;
}
ll de[6] = {0,1,2,4,5,6};
int main()
{
int T;
scanf("%d",&T);
while(T--){
ll R;
scanf("%lld",&R);
ll p[2][2]={1,1,1,0};
quick_pow(p,R+1);
ll ans = (res[0][0]-1+mod)%mod;
ll num = R/6;
ans = (ans-num*6+mod)%mod;
ans = (ans-8*solve(num)+mod)%mod;
ans = (ans-de[R%6]+mod)%mod;
printf("%lld\n",ans);
}
return 0;
}
D.二次函数
据说是初中数学题, f ( x ) = t a n ( x ) f(x)=tan(x) f(x)=tan(x)可解决,但没想出来,网上的题解也比较少。
暂未解决。
题意:假设有一个元素互不相同的正整数数组 a [ 1... n ] a[1...n] a[1...n],我们用以下方法得到数组 b [ 0... n − 1 ] b[0...n−1] b[0...n−1]:初始时 b [ i ] b[i] b[i] 都为 -1,我们对 i = 1... n i=1...n i=1...n 依次插入 a [ i ] a[i] a[i],假设现在要插入的数是 x x x,首先我们找到 x % n x\%n x%n这个位置,如果 b [ x % n ] = − 1 b[x\%n]=−1 b[x%n]=−1,则令 b [ x % n ] = x b[x\%n]=x b[x%n]=x,之后结束这次插入;否则看 b [ ( x + 1 ) % n ] b[(x+1)\%n] b[(x+1)%n] 是否等于 −1,如果等于则令 b [ ( x + 1 ) % n ] = x b[(x+1)\%n]=x b[(x+1)%n]=x,如果不等于,则继续看 ( x + 2 ) % n (x+2)\%n (x+2)%n…,直到找到一个位置。
完成所有插入后,我们会得到一个数组 b b b,现在给定这个数组 b b b,你需要求一个字典序最小的 a [ 1... n ] a[1...n] a[1...n]
思路:题意就是数据结构课本上的线性探查法,现场时候被线性探查四个字吸引住了。没想出正确解法,队友看出来这是一个拓扑排序题,关键在于建图,在队友帮助下把这题补了出来…
对于 b [ i ] b[i] b[i]而言,若 b [ i ] % n < i b[i]\%n <i b[i]%n<i,说明位置 b [ i ] % n − i b[i]\%n-i b[i]%n−i上的数字都先于 b [ i ] b[i] b[i]插入。
若 b [ i ] % n > i b[i]\%n>i b[i]%n>i,说明位置 1 − ( b [ i ] % n − 1 ) 1-(b[i]\%n-1) 1−(b[i]%n−1)和 i − n i-n i−n上的数字都先于 b [ i ] b[i] b[i]插入。
按照拓扑序列的输出方法,输出一个字典序最小的拓扑排序即可。
#include
using namespace std;
const int maxn = 1e3+5;
vectorson[maxn];
struct node{
int num,b,sum;
}G[maxn];
struct cmp{
bool operator()(const node &X,const node &Y){
return X.b > Y.b;
}
};
int main()
{
int n;
scanf("%d",&n);
for(int i = 0;i i){
for(int j = G[i].b%n;j,cmp> Q;
for(int i = 0;i
题意:给定长度为 n 的两两不相同的整数数组 b [ 1... n ] b[1...n] b[1...n],定义 f ( y ) f(y) f(y) 为:将 b b b 每个位置异或上 y y y 后,得到的新数组的逆序对个数。现在你需要求 ∑ i = 1 m f ( i ) \sum_{i=1}^{m}f(i) ∑i=1mf(i)由于答案可能很大,你只需要输出答案对 998244353 取模后的值。
思路:考虑原始序列中每两个点之间对答案的贡献。对于 b [ i ] b[i] b[i]与 b [ j ] b[j] b[j]( i < j i<j i<j)两个数字而言,假设 b [ i ] , b [ j ] b[i],b[j] b[i],b[j]二进制中最高位不同位是第 k k k位:若 b [ i ] < b [ j ] b[i]<b[j] b[i]<b[j],则第 k k k位为1的数与它们异或以后会产生一个逆序对,若 b [ i ] > b [ j ] b[i]>b[j] b[i]>b[j],则第 k k k位为0的数与它们异或以后,会产生一个逆序对。
问题转换成了求 1 − m 1-m 1−m中二进制第 k k k位为1的数字有多少个。这个需要想一想。
考虑从最后一位开始向第 k k k位枚举,设当前位为 j j j,如果第 j j j位为1,则说明当第 j j j位为0时,余下的 j − 2 j-2 j−2(第 k k k位固定为1)位是可以任取的,共有 1 < < ( j − 1 ) 1<<(j-1) 1<<(j−1)个不同的数字。当模拟到第 k k k位时,此时判断第 k k k位如果是1,则当其后面均为零的数字与m之间的数字,都是第 k k k位为1的数字,具体可看代码实现。
#include
using namespace std;
int sum[32];
const int mod = 998244353;
int pos = 0;
void init(int m){
memset(sum,0,sizeof sum);
while(m > (1<=0;i--)
for(int j = pos;j>=i;j--)
if(i == j){
if(m & 1<
题意:一共有m个红包,n个人,其中至少有一个机器人。给出每个红包抢到的顺序名单,比机器人快的都是机器人,机器人也有可能没有抢这个红包,问最少可能有几个机器人?
思路:签到题,枚举第几个是机器人,然后递归搜索顺序名单,把该方案是机器人的人都加起来,类比n个答案,选出最小的那个, n , m < 100 n,m<100 n,m<100,暴力去做的…代码就不贴了…
H.同构
据说是跟补图有关的题目。
暂未解决。
题意:求偶数位比相邻两个数都大的,长度为n的 1 − n 1-n 1−n 排列个数, n n n一定是奇数。由于答案可能较大,只需要输出答案对 998244353 取模后的值。
思路:考虑 s u m [ i ] sum[i] sum[i]为长度为 i i i的强壮排列,从 1 − i 1-i 1−i为最大的数可能摆放的位置,则从 1 − i 1-i 1−i枚举 j j j, s u m [ i ] + = C i − 1 j − 1 ∗ s u m [ j − 1 ] ∗ s u m [ i − j ] sum[i]+=C{_{i-1}^{j-1}}*sum[j-1]*sum[i-j] sum[i]+=Ci−1j−1∗sum[j−1]∗sum[i−j],从 i − 1 i-1 i−1个数中选出 j − 1 j-1 j−1放到第 j j j位以前排,变成两种长度的序列进行组合。
n 2 n^2 n2的复杂度,会超时,于是我们就愉快的打表,而且只有奇数长度的排列,表是存的下的…
#include
#define ll long long
using namespace std;
const int mod = 998244353;
const int maxn = 1e5+5;
ll sum[maxn],inv[maxn],facts[maxn];
ll quick_pow(ll a,ll n){
ll res = 1;
while(n>0){
if(n&1)
res = res*a%mod;
a = a*a%mod;
n>>=1;
}
return res;
}
void init(){
facts[0] = 1;
inv[0] = 1;
for(int i = 1;i