Logo __vector__ 的博客

博客

标签
暂无

论如何 AK 2024.5.4 Luogu Div.3

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-05-04 16:25:39

AK 了,感觉比较偏于诈骗题。

T1

分类讨论原始时间为 $6:00$ ~ $23:59$ 和 $0:00$ ~ $5:59$ 就好了。

#include <bits\/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mkpr make_pair
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
int T;
int h,m;
void print(int x){
    if(x<10){
        putchar('0');
    }
    printf("%d",x);
}
void solve(){
    scanf("%d:%d",&h,&m);
    if(h>=6&&h<=23){
        print(h);
        putchar(':');
        print(m);
        putchar(10);
    }else{
        h+=24;
        print(h);
        putchar(':');
        print(m);
        putchar(10);
    }
}
int main()
{
	T=1;
	while(T--){
		solve();
	}
	return 0;
}

T2

打表发现,随着 $p$ 的增加,答案先降后升。
然后做完了。

#include <bits\/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mkpr make_pair
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
int T;
pair<double,double> midnode(double x1,double y1,double x2,double y2,double p){
	\/\/ (x1,y1) 到 (x2,y2) 的到 p 位置的节点
	if(abs(x2-x1)<1e-10){
		\/\/ 竖线,不能再这样用
		return make_pair(x2,y1+(y2-y1)*p);
	}
	double k=(y2-y1)\/(x2-x1);
	double b=y1-k*x1;
	double mid_x = x1+(x2-x1)*p;
	double mid_y = k*mid_x+b;
	return make_pair(mid_x,mid_y);
}
double dis(pair<double,double> u,pair<double,double> v){
	return sqrt((v.first-u.first)*(v.first-u.first)+(v.second-u.second)*(v.second-u.second));
}
double hl(double a,double b,double c){
	double _p=(a+b+c)\/2.0;
	return sqrt(_p*(_p-a)*(_p-b)*(_p-c));
}
double calc(double x1,double y1,double x2,double y2,double x3,double y3,double p){
	auto f=midnode(x1,y1,x2,y2,p);
	auto d=midnode(x2,y2,x3,y3,p);
	auto e=midnode(x3,y3,x1,y1,p);
	double a=dis(f,d);
	double b=dis(d,e);
	double c=dis(e,f);
	return hl(a,b,c);
}
void solve(){
	double l,r,x1,y1,x2,y2,x3,y3;
	scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&l,&r,&x1,&y1,&x2,&y2,&x3,&y3);
	if(r<0.5){
		printf("%.10lf\n",calc(x1,y1,x2,y2,x3,y3,r));
	}else if(l>0.5){
		printf("%.10lf\n",calc(x1,y1,x2,y2,x3,y3,l));
	}else{
		printf("%.10lf\n",calc(x1,y1,x2,y2,x3,y3,0.5));
	}
}
int main()
{
	T=1;
	while(T--){
		solve();
	}
	return 0;
}

T3

  • 如果有偶数个不同质因数,那么两个一组删掉,显然答案是 $1$。
  • 否则,如果只有一个质因数,那么无法操作,答案是输入的数。
  • 否则,有不同质因数的数量大于等于 $3$,且数量为奇数,那么考虑这样操作:选择两个因数,先将它们在接下来的操作中排除。然后,对于剩下的因数(显然是奇数个),两两一组消掉,最后只会剩下一个因数 $p$。
    然后从一开始选择的两个因数中选择一个,将其次数减一,并消掉 $p$。然后最后一次操作,将一开始选择的两个因数同时选择,消掉,最后答案是 $1$。
    但是,上述操作能实现的前提是至少有 $1$ 个质因数的次数大于 $1$,否则答案是最小的质因数的值。

复杂度 $O(\sqrt n)$,瓶颈在于质因数分解。

#include <bits\/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mkpr make_pair
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
\/*
case1:偶数个不同因数,那么答案是 1
case2:1 个不同因数,答案是这个因数
case3:>=3 个奇数个不同因数:  
那么考虑保留两个不同因数,剩下互相 pvp。  
这要求,有一个因数次数>=2
*\/
int T;
int x;
void solve(){
    scanf("%d",&x);
    int oldx=x;
    vector<pii> div;
    int mxp=0,mndiv=1e9;
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            int cnt=0;
            while(x%i==0){
                cnt++;
                x\/=i;
            } 
            mndiv=min(mndiv,i);
            mxp=max(mxp,cnt); 
            div.emplace_back(make_pair(i,cnt));
           \/\/ printf("%d ^ %d\n",i,cnt);
        }
    }
    if(x>1){
        mndiv=min(mndiv,x);
        div.emplace_back(make_pair(x,1));
     \/\/   printf("%d ^ %d\n",x,1);
    }
    if(div.size()%2==0){
        puts("1");
    }else{
        if(div.size()==1){
            printf("%d\n",oldx);
        }
        else if(mxp>1){
            puts("1");
        }else{
            printf("%d\n",mndiv);
        }
    }
}
int main()
{
	T=1;
	while(T--){
		solve();
	}
	return 0;
}

T4

有一个结论:如果数组总和大于等于 $0$,那么这个数组合法。
证明:

显然,设 $p$ 为最小前缀和,$s$ 为最大后缀和,那么如果 $p+s \ge 0$,那么序列合法。
如果 $p \ge 0$,那么显然,由于 $tot \ge 0$,序列合法。
现在探讨 $p \lt 0$ 的情况。
假设 $p = \sum\limits_{i=1}^{k} a_i $,设总和 $tot = \sum a_i$,那么显然 $k+1$ 开头的后缀和是 $x = tot - p$,易得 $x+p = tot \ge 0$。

所以,只有 $1,2$ 操作是有效的,排个序贪心就行,注意别删完。

#include <bits\/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mkpr make_pair
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
int T;
const int maxn=1e5+5;
int n;
ll a[maxn];
ll p,q,r;
void solve(){
	scanf("%d",&n);
	scanf("%lld%lld%lld",&p,&q,&r);
	ll tot=0;
	FOR(i,1,n){
		scanf("%lld",&a[i]);
		tot+=a[i];
	}
	if(n==1){
		if(tot>=0){
			puts("0");
		}
		else printf("%lld\n",-tot*p);
		return;
	}
	if(tot>=0){
		puts("0");
	}else{
		int delcnt=0;
		sort(a+1,a+n+1);
		ll ans=0;
		FOR(i,1,n){
			if(a[i]<0){
				ll cha=min(-a[i],-tot);\/\/ 当前元素变成 0(或者,只要把 tot 变成 0)  需要增加多少次
				ll hf1=cha*p;\/\/ 把当前元素变成 0  的代价 1
				ll hf2=q;\/\/ 变成 0 的代价 2
				tot+=cha;
				if(delcnt==n-1){
					ans+=hf1;
				}else{
					ans+=min(hf1,hf2);
					delcnt+=(hf2<hf1);
				}
			}
		}
		ans+=(-tot*p);
		printf("%lld\n",ans);
	}
}
int main()
{
	T=1;
	while(T--){
		solve();
	}
	return 0;
}

ARC177 记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-05-14 16:25:21

废话区

赛时 40min 切了 ABC,赛时切了 AB 后以为稳上分,就摆了一会再去看 C,最后掉分了,搞得猝不及防。

翻榜发现就比 Register_int 多 2min 罚时,重温了去年省选以 0.01 分差败于 zd 大佬的感觉。

A

设第 $i$ 种硬币价值是 $a_i$。

注意到对于 $\forall i \in {[1,5]},a_i | a_{i+1}$。

一个比较显然的贪心,即每次尽可能选价值大的硬币。

因为,越小的硬币适用的价格范围越广。

B

现在,给定一个全 0 的数组,给定了一些 $1$ 的位置,要求将对应位置的数变成 $1$。

正着操作似乎不太好操作。

考虑倒着来,依次处理倒数第 $1$ 个 $1$,倒数第 $2$ 个 $1$,$\cdots$,倒数第 $1$ 个 $1$。

假设从前到后第 $i$ 个 $1$ 位置在 $p_i$。
考虑现在要处理第 $t$ 个 $1$,且第 $t+1$ 个及以后的 $1$ 已经处理完毕,第 $1$ 到 $t$ 个 $1$ 尚未处理。

显然,可以先使用 $p_t$ 次 $A$ 操作将前 $p_t$ 个位置赋值成 $1$,然后再使用 $p_t-1$ 次 B 操作将前 $p_t-1$ 个位置赋值成 $0$。

以上反复操作,可以得到一组正确答案。

C

两个人,第一个要从 $(1,1)$ 走到 $(n,n)$,第二个要从 $(1,n)$ 走到 $(n,1)$,第一个人只能走红色或紫色格子,第二个人只能走蓝色或紫色格子。

现在要求最多把多少个格子变成紫色,使得两个人都能走到目标点。

事实上,有效的操作一个格子,只能使其中一个人的可使用格子增加。

也就是说,两个人的操作是独立的,分别建图跑最短路求答案,然后加起来就可以了。

我赛时的丑陋代码

D

本题 @Iceturky 教会了我做法,@cxm1024 的代码给了我一个较为简洁易懂的参考,使得我的代码长度压缩了 3 倍,学会了一个较为简洁的 mint 实现。在此拜谢。

注意,如果一些横坐标升序排序后编号连续的点,满足相邻点距离小于等于 $H$,那么这些点就可以互相影响,可以把它们放进一个连通块。

现在,依次枚举 $1$ 到 $n$ 次地震,同时维护当且地震结束后,所有电线杆倒塌的概率。

现在,考虑什么情况下,刚好第 $i$ 次地震结束后,$i$ 所在的连通块被全部摧毁。接下来分类讨论:

  • 答案不为 $0$ 前提
    $i$ 电线杆所在连通块中,横坐标和编号都比 $i$ 点对应值小的电线杆都不能向右倒塌;所有横坐标大于 $i$ 电线杆,且编号小于 $i$ 电线杆的电线杆都不能向左倒塌。
  • 若第 $i$ 个电线杆向左倒塌
    第 $i$ 个电线杆所在连通块中,横坐标比 $i$ 电线杆小的电线杆直接全部清除。
    那么要么第 $i$ 个电线杆处于所在连通块的最右端,要么第 $i$ 个电线杆按横坐标排序的下一个电线杆的编号小于 $i$,才能保证右边的全部清除。
  • 若第 $i$ 个电线杆向右倒塌
    和上一种情况同理。

我自认为 cxm 大佬的代码是比大多数题解清楚的,所以超级巨佬 cxm1024 的赛时提交

E

官方题解说只有一百多种有效的题目分数分布,暂时没理解。

F

Codeforces Round 945 (Div. 2) 记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-05-18 17:08:23

废话区

真希望这场没读错 D 题,或者先去做 E。

选任意一个选项,用大号打都能紫,然而我用的小号,而且 D 读错题了,E 赛时直接没看,再加上 C 对着题目保证不存在的 $n$ 为奇数的情况分讨了很久,perf 只有 1700.

A

首先判定总和为奇数的情况无解。

然后每次贪心删最大的两个,由于数据小,懒得推式子的话,multiset 模拟就可以。

B

有两种做法,我赛时写的是 $O(n \log (\max a_i) \log n)$,另一种是 $O(n \log (\max a_i))$。

我赛后测试,看着差了一个 log,但后者速度是前者两倍。

  • 做法 1: 如果写一个暴力程序模拟样例,那么会发现存在单调性,即存在一个分界点,$k$ 比这个小一定不符合要求,比这个大一定符合要求。
    关于证明,假设现在有一个 $k_1$ 满足 $k=k_1$ 满足条件,那么事实上,经过一定思考可以比较容易得出这个结论(下一个做法用到):$\forall i \in [1,n-k_1+1] $,满足 $a_i | a_{i+1} | a_{i+2} | \cdots | a_{i+k_1-1} = a_1 | a_2 | a_3 | \cdots | a_n$。
    现在,每个位置开头的连续 $k_1$ 个的数的 or 值还要再 or 下一个数,而每个位置新 or 的数显然已经包含在自身了,所以不会改变自身原本的值。
    现在就可以二分求解本问题了。 赛时记录
  • 做法 2:
    根据做法 1 的结论,对于一个合法的 $k$,$\forall i \in [1,n-k+1] $,满足 $a_i | a_{i+1} | a_{i+2} | \cdots | a_{i+k-1} = a_1 | a_2 | a_3 | \cdots | a_n$。
    可以提前计算出整体 or 值,然后对于每个位置,计算这个位置向后延申多少个位置,or 值等于整体 or 值,最后对于所有位置,这个值取 max 就是答案。
    赛后记录

C

我赛时的分讨结论:不论 $n$ 奇偶,最优答案一定是 $2,4,6,8,\cdots$ 位置为局部 max,或 $3,5,7,9,\cdots$ 位置为局部 max。

先假设已经选好了第一种还是第二种,最劣情况是选中的值是 $1,2,3,\cdots \lfloor \frac{n-1}{2}\rfloor$,而 $1,2,3,\cdots$ 紧挨着的是 $n,n-1,\cdots,\lfloor \frac{n-1}{2} \rfloor+1$。

由于值为 $1,n$ 相邻,导致无论如何值为 $1$ 的得放弃,如果不放弃,还会连锁反应导致其他的也废掉。

考虑第一种,如果第 $2,4,6,8,\cdots$ 位置中,不存在一个位置使得值为 $1$,且旁边有值为 $n$,那么第一种构造是最优解。

反之,如果存在呢?

那就跳过它就好了,选第二种构造,因为只要选中的数不存在 $1$,那么我们希望构造的局部最大值显然可以构造成全部为 $n+2$,然后将非局部最大值全部弄成小于等于 $n-1$。

赛时记录

D

赛时看成了所有加起来等于 $m$,非常遗憾没有通过这道送分题。

看到这题,我第一个想到的是根据最大值。

事实上可以通过 $n$ 次询问求出最大值。

然后,注意,划分成 $k$ 段,每段的值都等于 $m$,且同时取决于它的最大值和它的长度。

那么注意,整体最大值肯定也是它所属的段的最大值。

所以,只需要枚举整体最大值所在段的长度,并判定是否合法就可以。

比较容易得出,整体最大值所在段的长度一定不超过 $\frac{n}{k}$

然后,每次判定需要 $k$ 次询问,将前后询问总数加起来,$\lfloor \frac{n}{k} \rfloor \cdot k + n \le 2n$,可以通过本题。
赛后记录

E

这题感觉也就 1800 难度,为啥放 E 啊赛时没机会看。

考虑 $3$ 个数 $a \lt b \lt c$。
其中 $a,b$,$a,c$ 可以交换,而 $b,c$ 不能交换。
草稿纸上模拟一下,可以发现事实上 $a,b,c$ 可以通过一定操作任意交换!

然后就有了本题解法。

将能交换的两个数放到同一个连通块,最后同一个连通块内的所有数都可以互相交换。

草稿纸上模拟一下 $l \neq r,l \le n+1$ 的情况。

然后发现了一个惊喜: $\forall i \in [1,r-1]$ 都处于同一个连通块!

那么对于 $l \neq r$ 且 $l \gt n+1$ 呢?

略微思考可得 $\forall i \in [l-n,r-1]$ 处于同一个连通块。

所以说,对于 $l \neq r$ 的部分,只需要枚举 $r$,那么 $l$ 就是从 $1$ 开头的一段合法的区间,使得需要囊括所有的错位的数字值域,很容易计算。

再来想想 $l = r$ 的部分。

考虑先找出任意一对原序列中需要互相交换位置的数字 $x,y$。

如果存在 $l=r$ 的解,那么这个 $r =x+y$ 即 $r = x+y$

接下来,就是对于所有需要互相交换的数字,判断和是否为 $x+y$。

如果全部满足,那么存在一组 $l=r = x$ 符合要求。
赛后记录

F

暂时不会。

CF1974G 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-05-21 18:54:16

因为二模没打这场比赛,结果 vp 无伤 AK 了,哈哈。

想法 1

考虑 dp。
但是,可以发现无论如何都无法设计状态,使得状态总数在一个可控的范围内,故抛弃 dp。

想法 2

考虑这个问题的一个解,该怎么优化,才能使其变得更优秀。

注意到,如果某天以 $d$ 的代价购买了一个单位,那么如果后面某天单价 $e \lt d$,但是由于这天购买所花费的钱,而导致那天无法购买,那么肯定是不优的。

所以做法:维护一个优先队列,记录目前的购买状态。

从 $1$ 到 $m$ 天遍历,如果第 $i$ 天可以用剩余的钱直接购买,那么算进答案并加入队列;否则,看第 $i$ 天是否能替代掉队列中的一个元素。

#include <bits\/stdc++.h>
using namespace std;
const int maxn=2e5+5;
typedef long long ll;
int m,x;
int c[maxn];
void solve(){
	scanf("%d%d",&m,&x);
	for(int i=1;i<=m;i++){
		scanf("%d",&c[i]);
	}
	priority_queue<int> q;
	int s=0;
	for(int i=1;i<=m;i++){
		if(s>=c[i]){
			s-=c[i];
			q.push(c[i]);
 
		}else{
			if(!q.empty()&&q.top()>c[i]){
				s+=q.top();
				q.pop();
				s-=c[i];
				q.push(c[i]);
 
			}
		}
		s+=x;
	}
	printf("%d\n",int(q.size()));
}
int main(){
	int _;
	scanf("%d",&_);
	while(_--){
		solve();
	}
	return 0;
}

2024.5.23 vp 记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-05-23 09:41:54

对 2300 以下或者我做出来的题目进行记录。

  • Codeforces Round 889 (Div. 1)
    A1:将最大整数 $5$ 次翻倍,然后把所有负数变成正数,然后前缀和。
    特判全负,此时后缀和。
    A2:注意负数变正数的最劣情况下,可能无法完成,但容易证明此时,倒过来(正变负),是可以完成的。
    B:一旦长度确定,答案也就确定,考虑维护哪些长度可用。
    看着挺难搞,考虑 bitset 维护长度可行性。
    考虑 $1$ 到 $n$ 将 $a_i$ 加入答案,注意一个数 $a_i$ 能对长度造成贡献当且仅当已有长度大于等于 $i$,所以新开一个数组记录每个长度是否可行,然后原来的 dp 数组每枚举一个 $i$ 就把 $dp_i$ 设置为 0。
    Div.1 赛时过 A1,A2,B 的 perf 在 2300~2500,然而 vp 被 B 卡死在了转移方程的设计,原因大概是因为脑子不清醒 + 对此类型 dp 的不熟练。
  • Codeforces Round 880 (Div. 1)
    A \*1700 ,用了 10min 切掉,做法是枚举第一个数,然后确定第二个数的范围,不断用 $k$ 减去范围大小直到为 $0$。
    B 难度太大,没通过。
    现场参赛 perf 在 2200~2300,主要是本场难度跨度太大,区分度太低。

CF1978F 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-06-18 09:05:36

这场 Div.2 CDEF 难度是反过来的。
本做法复杂度可能劣于正解,但是能过。

  • 模拟样例。
    这个是倒数第二个样例:

    相同颜色圈住的数字代表相同连通块。
    容易发现,每个数组都构成了 $1$ 或 $2$ 条对角线。
    容易得出结论,左上角到右下角的对角线是由第一个元素组成,由此向左的每条对角线分别是第 $5,4,3,2$ 个元素构成的。
    由此向右的每条对角线分别是第 $2,3,4,5$ 个元素构成的。

  • 结论
    注意除了 $1$ 号元素以外,所有元素都分别组成了 $2$ 条对角线,分别位于 $1$ 号元素对角线的左边和右边,为了方便,暂时称 $1$ 号元素对角线左边的对角线成为左对角线,右边同理。
    将下面所述 $1$ 号构成的左对角线和右对角线看作同一条对角线。
    由上述,第 $1,2,3,4,5$ 个元素组成的右对角线的依次相邻(前后距离为 $1$)。第 $1,5,4,3,2$ 个元素组成的左对角线依次相邻。
    形式化,$1,2,3,\cdots,n$ 个元素组成的右对角线前后距离为 $1$,另外 $1,n,n-1,\cdots,2$ 的前后距离也是 $1$。
    由于 $k \ge 2$,除了值为 $1$ 的元素,所有元素组成的单独一条对角线(左对角线和右对角线独立)都是同一个连通块。

  • 做法
    问题是如何处理所有非互质的数对。
    看到 $a_i \le 10^6$,这个限制意味着可以在调和级数复杂度内预处理每个数的质因数,随后每个 testcase 只需要枚举所有出现过的质因数,并记录包含这个质因数的所有元素,按照上述规则建边就可以了,然后按照上述结论建边就可以了。
    最后 dfs 一遍求出连通块个数。

  • 小细节
    值为 $1$ 的所有元素都是不同连通块,一个 $1$ 就能贡献 $n$ 个连通块,$1$ 应该被单独计算。

最后复杂度是一开始的预处理质因数,再加上每个测试数据的所有元素的质因数个数总和,大量 STL 的使用也不影响通过。

#include <bits\/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mkpr make_pair
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
int T;
template<class T>
void ckmx(T& a,T b){
    a=max(a,b);
}
template<class T>
void ckmn(T& a,T b){
    a=min(a,b);
}
typedef vector<int> vi;
typedef vector<ll> vl;
#define fi first
#define se second 
template<class T>
T gcd(T a,T b){
    return !b?a:gcd(b,a%b);
}
template<class T>
T lcm(T a,T b){
    return a\/gcd(a,b)*b;
}
\/*1,2,3,4,5
5,1,2,3,4
4,5,1,2,3
3,4,5,1,2
2,3,4,5,1
可以注意到,从 1 形成的左上-右下对角线向左右延申,向左分别是 5,4,3,2 形成的。  
向右分别是 2,3,4,5 形成的。  
同一个对角线全部连接。  
1-5-4-3-2 或 1-2-3-4-5 对角线距离是 1   
本质上,可以枚举所有出现过的质因数,枚举这个质因数对应的所有元素。  
然后枚举所有相邻编号元素,单独处理 1 号对角线
考虑 1~1e6 所有数的质因数。
*\/
const int maxn=1e6+5;
int n,k,a[maxn];
vector<int> prims[maxn];
bool vis[maxn];
vector<int> div_to_elem[maxn];
vector<int> g[maxn*2];
void con(vi& ids){
	\/\/ ids 所有元素 gcd 不为 1,且按照元素 id 升序
	for(int& id:ids){
		if(id==1)continue;
		if(id-1+(n-id+1)<=k){
			g[id].emplace_back(id+n);
			g[id+n].emplace_back(id);
		}
	}
	if(ids.size()==1)return;
	\/\/for(int& id:ids)printf("id %d\n",id);
\/\/	puts("==========");
	for(int i=1;i<ids.size();i++){
		if(ids[i]-ids[i-1]<=k){
			g[ids[i]].emplace_back(ids[i-1]);
			g[ids[i-1]].emplace_back(ids[i]);
		\/\/	printf("con = z(%d) z(%d)\n",ids[i],ids[i-1]);
		}
	}
	if(ids.front()==1){
		reverse(ids.begin()+1,ids.end());
		for(int i=1;i<ids.size();i++){
			if((ids[i-1]==1?(n+1):ids[i-1])-ids[i]<=k){
				g[ids[i]+n].emplace_back(ids[i-1]+n);
				g[ids[i-1]+n].emplace_back(ids[i]+n);
			\/\/	printf("con = f(%d) f(%d)\n",ids[i],ids[i-1]);
			}
		}	
	}else{
		reverse(ids.begin(),ids.end());
		\/\/ mx,mx-1,...,mn
		if(n-ids.front()+1+ids.back()-1<=k){
			g[ids.front()+n].emplace_back(ids.back());
			g[ids.back()].emplace_back(ids.front()+n);
		}
		for(int i=1;i<ids.size();i++){
			if(((ids[i-1]==1?(n+1):ids[i-1]))-(ids[i])<=k){
				g[ids[i]+n].emplace_back(ids[i-1]+n);
				g[ids[i-1]+n].emplace_back(ids[i]+n);
			\/\/	printf("con = f(%d) f(%d)\n",ids[i],ids[i-1]);
			}
		}	
	}
}
bool dfsvis[maxn*2];
void dfs(int u){
	dfsvis[u]=1;
	for(int& v:g[u]){
		if(!dfsvis[v])dfs(v);
	}
}
void solve(){
	scanf("%d%d",&n,&k);
	ll ans=0;
	FOR(i,1,n){
		scanf("%d",&a[i]);
		if(a[i]==1){
			ans+=n;
		}
	}
	vector<int> divs;
	FOR(i,1,n){
		for(int& div:prims[a[i]]){
			if(!vis[div]){
				vis[div]=1;
				divs.emplace_back(div);
			}
			div_to_elem[div].emplace_back(i);
		}
	}
	if(a[1]!=1)g[1].emplace_back(n+1);
	for(int& div:divs){
		con(div_to_elem[div]);
	}
	
	FOR(i,1,2*n){
		if(a[((i>n)?(i-n):(i))]!=1&&!dfsvis[i]){
			ans++;
			dfs(i);
		}
	}
	printf("%lld\n",ans);
	\/\/ clear
	for(int& div:divs){
		vis[div]=0;
		div_to_elem[div].clear();
	}
	FOR(i,1,2*n){
		g[i].clear();
		dfsvis[i]=0;
	}
}
int main()
{
	FOR(i,2,1000000){
		if(prims[i].empty()==0)continue;
		for(int j=i;j<=1000000;j+=i){
			prims[j].emplace_back(i);
		}
	}
	scanf("%d",&T);
	while(T--){
		solve();
	}
	return 0;
}

ZOI Round 1 & 新春联欢赛记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-15 20:36:13

以此纪念从学 OI 开始,第一次赛时 AK 比赛,从看题开始用了 1h。

但是有 129 个 AKer,无语。

A

可以计算出 $[1,r]$ 和 $[1,l-1]$ 的答案,忽略题目的下限要求。

然后 $2,3,5,7$ 是质数,枚举它们的指数就可以了。

B

算出每个站第一次到达时间,接下来每 $k$ 时间后再来一次。

简单分讨。

#include <bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,k,q;
int a[maxn];
ll pre[maxn];
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>k>>q;
	for(int i=1;i<n;i++){
		cin>>a[i];
	}
	for(int i=2;i<=n;i++){
		pre[i]=pre[i-1]+a[i-1];
	}
	while(q--){
		int x,t;
		cin>>x>>t;
		if(t<=pre[x]){
			cout<<pre[x]-t<<'\n';
		}else{
			t-=pre[x];
			if(t%k==0){
				cout<<"0\n";
			}else{
				cout<<k-t%k<<'\n';
			}
		}
	}
	return 0;
}  

C

与最短路板子的区别,就是它必须得以一个大整体运动。
随便选一个红点,枚举出它放在哪个地方,整体随之运动能完成目标。
然后最短路。

D

设距离是 $x$,时间是 $t$。
显然需要进行 $x$ 次有效运动。
剩下的 $t-x$ 时间,得想办法消磨掉,同时保证位置最终不变。
很显然其中 $\frac{t-x}{2}$ 时间用来反向运动就 ok 了。

总共运动 $t$ 次,$\frac{t-x}{2}$ 次反向运动,显然答案是 $C_{\frac{t-x}{2}}^t$

如果 $t \lt x$,无解。
如果 $t = x = 0$,无解。
如果 $t-x$ 是奇数,无解。

【LGR-175-Div.4】洛谷入门赛 #20

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-17 10:17:31

AK 了,本来不想写的,但 lxn 让去给隔壁讲题,所以就写了。

E

待判定 01 串大概是这个样子:00100100010001010000
题目限制最多有 $n$ 个连续的 $0$。

  • 如果字符串不存在 $1$:
    那么只要字符串长度小于等于 $n$ 就合法。

  • 否则:

    • 不能有连续超过 $n$ 个 $0$。
    • 任意两个相邻 $1$ 之间的 $0$ 数量都必须相同,且不能少于前导零数量,记相邻两个 $1$ 之间有 $x$ 个 $0$。
    • 对于最后一段 $0$,如果 $x$ 存在,那么长度不能大于 $x$。

F

可以预处理每个小朋友每年的身高。

G

翻转偶数次和没翻转一样。

H

可以计算出每个损坏的牛棚修补的代价,优先补充代价低的。

2024.2.18 梦熊模拟赛 T1 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-18 13:33:43

这道题我记得原题是图灵杯(CF 也有),并且洛谷上有,这道题应该是个 well-known。
当时爆零,没补题
这次奋战 4h(晚了 0.5h 参赛,算到赛后 0.5h)通过了这道题,写题解记录一下。

为了迎接寒冬,Farer John 准备了 堆木材用来修补他牛圈的围栏!其中第 块木材的长度为 $a_i$。虽然木材的长度参差不齐,但是 Farer John 不想把围栏修补坑坑洼洼的形状,而且要修建的围栏有两条。因此 Farer John 决定把这 堆木材分成两排,每排的木材的长度依次能构成一个公差非负的的等差序列。但是堆积的木材实在是太多了!Farer John 感到无从下手,于是奶牛 Bassie 邀请了你来制订方案,或是判断不存在合法的方案。

设 $1$ 号点在第一个等差序列,枚举第一个等差数列的第二个点。

由此计算出第一个等差数列的公差 $d$。

然后不断跳转到第 $3$ 个点,第 $4$ 个点,$\cdots$

怎么跳转到下一个符合要求的点是个关键。

假设当前点 $a_i$,那么下一点应该选择 $a_j = a_i +d$,使得 $j \gt i$
也就是说,我们需要对于 $1$ 到 $n$ 每个点,维护从这个点开始,每种可能的权值下一次出现的位置。

容易想到 $O(n^2)$ 做法,即从第 $n$ 个到第 $1$ 个倒叙枚举,转移显然。

但是显然过不去,于是想到主席树。可以发现每个点 $i$ 都保存一个 $i$ 开始的线段树版本就可以,然后这个问题解决了。

那么第二序列如何判断呢?

可以预处理出每个点向前(延伸到 $j \le i$) 最多延伸多少。

然后在第一序列跳转过程中分情况讨论,判断第二序列合法性。

另外我们每跳转到一个点,都判断如果第一序列以此结尾,那么是否合法。
只要我们找出一个方案合法,那么立刻退出,如果当前方案一旦判断不合法,也立刻退出。

那么复杂度看上去似乎不对啊。

简单推导可得复杂度是 $\log n $ 乘 $1$ 序列跳转总次数。

我能想到的让跳转总次数最长的构造方式:$1,2,3,4,5,6,7,8,9,\cdots$
这样跳转总次数是 $O(n+\frac{n}{2}+\frac{n}{4}+\cdots) = O(2n) = O(n)$

总复杂度 $O(n \log n)$

如果错了,请大佬们指出。

\/\/ By __vector__ (luoguid = 507348) , real name = liumonong
#include <bits\/stdc++.h>
using namespace std;
const int inf=INT_MAX;
const int maxn=1e5+5;
int n;
int a[maxn];
struct Node{
    int ls,rs,l,r,val;
};
struct HJT{
    Node node[maxn*25];
    int nodecnt=0;
    int rt[maxn];
    inline int ls(int pos){
        return node[pos].ls;
    }
    inline int rs(int pos){
        return node[pos].rs;
    }
    int build(int l,int r){
        int now=++nodecnt;
        node[now].l=l,node[now].r=r;
        if(l==r){
            node[now].val=inf;
            return now;
        }
        int mid=(l+r)\/2;
        node[now].ls=build(l,mid);
        node[now].rs=build(mid+1,r);
        return now;
    }
    int chg(int oldnode,int x,int val){
        int nwnode=++nodecnt;
        node[nwnode]=node[oldnode];
        if(node[oldnode].l==node[oldnode].r){
            node[nwnode].val=val;
            return nwnode;
        }
        int mid=(node[oldnode].l+node[oldnode].r)\/2;
        if(mid>=x)node[nwnode].ls=chg(ls(oldnode),x,val);
        else node[nwnode].rs=chg(rs(oldnode),x,val);
        return nwnode;
    }
    int query(int pos,int x){
        if(node[pos].l==node[pos].r){
            return node[pos].val;
        }
        int mid=(node[pos].l+node[pos].r)\/2;
        if(mid>=x)return query(ls(pos),x);
        return query(rs(pos),x);
    }
}hjt;
struct Disc{
    int disc[maxn];
    int top;
    void init(int* a,int n){
        for(int i=1;i<=n;i++){
            disc[i]=a[i];
        }
        sort(disc+1,disc+n+1);
        top=unique(disc+1,disc+n+1)-disc-1;
    }
    int rank(int val){
        int pos=lower_bound(disc+1,disc+top+1,val)-disc;
        if(pos==top+1)return -1;
        if(disc[pos]!=val)return -1;
        return pos;
    }
    int rktoval(int rk){
        return disc[rk];
    }
}disc;
int len[maxn];
bool tag[maxn];
int main(){
   \/\/ freopen("fence.in","r",stdin);
    \/\/freopen("fence.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    if(n==2){
        printf("1\n%d\n1\n%d\n",a[1],a[2]);
        return 0;
    }
    sort(a+1,a+n+1);
    disc.init(a,n);
    for(int i=1;i<=n;i++){
 \/\/       printf("i = %d val = %d\n",i,a[i]);
        a[i]=disc.rank(a[i]);
    }
    hjt.rt[n]=hjt.build(1,n);
    for(int i=n-1;i>=1;i--){
        hjt.rt[i]=hjt.chg(hjt.rt[i+1],a[i+1],i+1);
    }
    for(int i=1;i<=n;i++){
        if(i==1){
            len[i]=1;
        }else{
            if(i==2){
                len[i]=2;
            }else{
                len[i]=2;
                if(disc.rktoval(a[i])-disc.rktoval(a[i-1])==disc.rktoval(a[i-1])-disc.rktoval(a[i-2])){
                    len[i]=len[i-1]+1;
                }
            }
        }
    }
    for(int sec=2;sec<=n;sec++){
          \/\/  printf("sec2 = %d\n",sec);
        if(sec-2>=1){
            if(len[sec-1]<sec-2){
                break;
            }
        }
        bool ok=1;
        int now=sec;
        int d=disc.rktoval(a[sec])-disc.rktoval(a[1]);\/\/ 1 序列公差
        int d2=-1;\/\/ 2 序列公差
        if(sec>=4)d2=disc.rktoval(a[sec-1])-disc.rktoval(a[sec-2]);
        int lst2=-1;
        if(sec>=3)lst2=a[sec-1];\/\/ 2 序列目前遍历到的最后一个数

        vector<int> seq1;
       \/\/ printf("i = %d\n",sec);
        seq1.emplace_back(1);
        while(now<n){

           \/\/ printf("now = %d d = %d\n",now,d);
            seq1.emplace_back(now);
            int _new=disc.rank(disc.rktoval(a[now])+d);
            if(_new==-1||hjt.query(hjt.rt[now],_new)==inf){
             \/\/   printf("fail can't find %d + %d = %d\n",disc.rktoval(a[now]),d,disc.rktoval(a[now])+d);
                \/\/ 不存在 _new,所以 now 是最后一个
                \/\/ 应该判断之后是否合法
                if(len[n]<n-(now+1)+1)ok=0;
                if(now==n-1){
                    \/\/ 2 序列最后一个单位只有一个数
                    if(d2!=-1){\/\/ 2 序列存在公差
                        if(lst2!=-1&&disc.rktoval(a[n])-disc.rktoval(lst2)!=d2){
                            ok=0;
                        }
                    }
                }else{
                    if(d2==-1){\/\/ 没有公差,先设定
                        d2=disc.rktoval(a[n])-disc.rktoval(a[n-1]);
                    }else{
                        if(disc.rktoval(a[n])-disc.rktoval(a[n-1])!=d2){
                            ok=0;\/\/ 判断最后一段是否满足公差
                        }
                    }
                    if(lst2!=-1&&disc.rktoval(a[now+1])-disc.rktoval(lst2)!=d2){
                        ok=0;\/\/ 连接处是否满足公差
                    }
                }
                break;
            }
            if(1){
                \/\/ 假设从 now 截止
                int oldd2=d2;
                bool ok2=1;
                if(len[n]<n-(now+1)+1)ok2=0;
                if(now==n-1){
                    \/\/ 2 序列最后一个单位只有一个数
                    if(d2!=-1){\/\/ 2 序列存在公差
                        if(lst2!=-1&&disc.rktoval(a[n])-disc.rktoval(lst2)!=d2){
                            ok2=0;
                        }
                    }
                }else{
                    if(d2==-1){\/\/ 没有公差,先设定
                        d2=disc.rktoval(a[n])-disc.rktoval(a[n-1]);
                    }else{
                        if(disc.rktoval(a[n])-disc.rktoval(a[n-1])!=d2){
                            ok2=0;\/\/ 判断最后一段是否满足公差
                        }
                    }
                    if(lst2!=-1&&disc.rktoval(a[now+1])-disc.rktoval(lst2)!=d2){
                        ok2=0;\/\/ 连接处是否满足公差
                    }
                }
                if(ok2){
              \/\/      printf("stop = %d\n",now);
               \/\/     printf("stop  = %d\n",now);
                    break;
                }else{
                \/\/    printf("failstopping = %d\n",now);
                    d2=oldd2;
                }
            }
            \/\/ 记得更新 lst2

                _new=hjt.query(hjt.rt[now],_new);
               \/\/ printf("_new = %d\n",_new);
                if(_new-now-1==1){
                    \/\/ 中间一个数
                    if(lst2==-1){
                        lst2=a[now+1];
                        now=_new;
                        continue;
                    }
                    if(d2==-1){
                        \/\/ 2没有公差,建立
                        d2=disc.rktoval(a[now+1])-disc.rktoval(lst2);
                    }else{
                        if(lst2!=-1&&disc.rktoval(a[now+1])-disc.rktoval(lst2)!=d2){
                            ok=0;
                            break;
                        }
                    }
                }else if(_new-now-1>=2){
                    \/\/ 中间超过两个数
                    if(len[_new-1]<_new-now-1)ok=0;
                    if(d2==-1){
                        d2=disc.rktoval(a[_new-1])-disc.rktoval(a[_new-2]);
                    }else{
                        if(disc.rktoval(a[_new-1])-disc.rktoval(a[_new-2])!=d2){
                            ok=0;
                        }
                    }
                    if(lst2!=-1&&disc.rktoval(a[now+1])-disc.rktoval(lst2)!=d2){
                        ok=0;
                    }
                }
                if(_new-now-1>=1)lst2=a[_new-1];
                now=_new;
                if(!ok)break;

        }
        if(ok){
            seq1.emplace_back(now);
        \/\/    printf("seq1.size() = %d\n",seq1.size());
            int cnt1=1;
            tag[seq1[0]]=1;
            for(int i=1;i<seq1.size();i++){
                if(seq1[i]!=seq1[i-1])tag[seq1[i]]=1,cnt1++;
            }
            printf("%d\n",cnt1);
            for(int i=1;i<=n;i++){
                if(tag[i])printf("%d ",disc.rktoval(a[i]));
            }
            printf("\n");
            printf("%d\n",n-cnt1);
            for(int i=1;i<=n;i++){
                if(!tag[i])printf("%d ",disc.rktoval(a[i]));
            }
            return 0;
        }
    }
    puts("-1");
    return 0;
}

蚌埠住了

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-02-21 14:10:46

ChatGPT 你在干什么????
这张图 @yaojiahao2022 给我的。

Q: 请你以 lsxhyyds 为主角构思一个小说
A: 故事的主角是一个叫做lsxhyyds的年轻人,他生活在一个名为诺瓦利斯的未来世界中。在这个世界里,科技高度发达,人们生活在由虚拟现实技术构建的全息城市中。城市的每一寸空间都充满了虚拟现实的投影,人们可以通过植入式芯片与这个虚拟世界互动。 lsxhyyds是一名天赋异禀的虚拟现实程序员,他在诺瓦利斯最大的科技公司“星辰科技”工作。这家公司是虚拟现实技术的领军者,他们开发了一款名为“星辰之眼”的虚拟现实设备,可以让人们体验到前所未有的沉浸式虚拟世界。 然而,随着“星辰之眼”设备的普及,人们开始沉迷于虚拟世界,忽略了现实生活。城市中出现了越来越多的“虚拟依赖症”患者,他们无法自拔地沉浸在虚拟世界中,忽视了现实世界的重要性。 lsxhyyds意识到了这个问题,他开始怀疑“星辰之眼”设备的设计是否存在缺陷,导致了人们对虚拟世界的过度依赖。他决心深入调查,揭露真相并寻找解决之道。 在调查的过程中,lsxhyyds发现了“星辰之眼”设备背后隐藏的秘密。原来,这款设备的设计者们为了追求商业利益,故意加入了一种名为“虚拟纠缠子”的程序代码,这种代码可以在玩家的大脑中植入信号,让他们产生对虚拟世界的过度依赖。 lsxhyyds决心揭露这一阴谋,并寻找到了一群志同道合的朋友,他们一起合作,设计了一款名为“自由之眼”的反制设备,可以解除“虚拟纠缠子”代码的影响,让人们重新回归现实世界。 在一场激烈的斗争中,lsxhyyds和他的朋友们最终成功揭露了“星辰之眼”设备背后的阴谋,并推出了“自由之眼”设备,帮助人们摆脱了对虚拟世界的过度依赖。他们成为了诺瓦利斯城市中的英雄,改变了整个城市对虚拟现实技术的看法,让人们重新认识到科技应该为人类的幸福和进步服务。 这就是lsxhyyds的故事,一个通过自己的努力和创造力改变世界的年轻人。

Q:

发挥想象 描述 NOI2020 现场
A:
NOI2020是一个科技盛会,吸引了来自世界各地的科技爱好者、创业者和专业人士。会场布置得非常现代化,大量的LED屏幕和悬挂的全息投影装置展示着最新的科技成果和创新产品。参会者们穿着时尚的智能穿戴设备,与会议中的人工智能助手进行交流,获取各种信息和日程安排。 会场内有各种展台,展示着最新的虚拟现实技术、人工智能应用、生物科技产品等。参会者可以亲身体验各种科技产品,比如通过虚拟现实眼镜探索未来城市的样貌,或者体验最新的智能家居设备。 在会议的讲台上,各国顶尖的科技专家和企业领袖们进行着精彩的演讲和分享。他们讨论着未来科技的发展趋势,探讨人工智能、区块链、生物科技等领域的创新应用,以及科技对社会发展的影响。 整个会场充满了创新和活力,人们在这里交流思想,分享创意,探讨未来。NOI2020成为了一个科技创新的盛会,为世界各地的科技爱好者提供了一个交流合作的平台,推动着科技的发展和进步。

共 320 篇博客