Gravatar
yrtiop
积分:2053
提交:304 / 803

考虑倍增。

$f(i,k), g(i,k)$ 分别表示从 $i$ 出发,走 $2^k$ 步经过的边权最小值和边权和。一开始 $f(i,0)=g(i, 0)=w_i$。

为了方便处理,我们定义 $to(i, k)$ 表示从 $i$ 出发,走 $2^k$ 步走到的点。$to(i, k)$ 显然是容易处理的。根据 $to(i, k)$ 预处理 $f(i, k), g(i, k)$ 即可。

时间复杂度 $O(n \log ⁡k )$。



题目3714  Analysis of Pathes in Functional Graph AAAAAAAAAA      6      评论
2023-09-06 18:30:01    
Gravatar
yrtiop
积分:2053
提交:304 / 803

题目名是一首歌。

最大值最小化,考虑二分答案,转向判断是否存在一条 $1\to n$ 的路径满足权值 $\le mid$。

我们考虑二分答案其实是给每条边加了一个限制,也就是权值为 $w_i$ 的边最晚在 $\lfloor \frac{mid}{w_i} \rfloor$ 时经过。之后不再合法。

我们考虑路径的形态,从而帮助我们理解这个问题。我们知道,如果 $1\to 2$ 和 $1\to 3\to 2$ 同时合法,那么我们一定会选择 $1\to 2$ 这条路径。

显然在合法的前提下,我们会尽可能地走最短路。这其实是一个贪心的思想。可以理解为,走最短路可以达到更多的后继状态,所以一定优于不走最短路。

于是变成了一个边权为 1 的最短路问题,bfs 求解即可。时间复杂度 $\mathcal O(n\log w)$。

这题有着高达 80 的 dp 部分分,并不是很理解为什么场上没有人打。


题目3910  Great Voyage AAAAAAAAAAAAAAAAAAAA      5      评论
2023-09-06 18:29:54    
Gravatar
阎思宇_1
积分:0
提交:0 / 0

#include<iostream>

#include<cstdio>

#include<algorithm>

using namespace std;

int main(){

   int n;

   cin>>n;

   long long a[20001],b[20001],l=0;//a数组存储起点,b数组存储终点,l表示最终长度

   for(int i=0;i<n;i++)

       cin>>a[i]>>b[i];

   sort(a,a+n);

   sort(b,b+n);//由于起点终点的顺序对答案不产生影响,对a数组和b数组进行排序

   for(int i=0;i<n;i++)

   {

       l+=b[i]-a[i];//加上当前线段长度

       if(i+1<n)//如果这条线段不是最后一条线段

           if(b[i]>a[i+1])//如果这条线段与前一条线段有重复

               l-=b[i]-a[i+1];//减去重复部分

   }

   cout<<l;//输出

   return 0;

}


题目3426  火烧赤壁      3      评论
2023-08-27 15:18:52    
Gravatar
Dy
积分:4
提交:2 / 3

//这是我提交AC的第一道题

//庆祝一下写一篇题解~有什么不对欢迎提出

//一道经典的kmp题,一定要记住模版

//每一次询问时间复杂度大约O(n)的,使用O2评测机不会超时的哦

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cstring>
#include<map>
#include<set>
#include<queue>
#include<ctime>
#include<deque>
#include<list>
#include<stack>
#include<utility>
#define MAXN 1919810
#define ite iterator
#define fs fixed<<setprecision
typedef long long ll;
using namespace std;

inline ll read(){ll x=0,f=1;char z;z=getchar();while(z<'0'||z>'9'){if(z=='-')f=-1;z=getchar();}while(z>='0'&&z<='9')x=x*10+z-'0',z=getchar();return x*f;}
inline void write(ll x){if(x<0){x=-x;putchar('-');}if(x>9){write(x/10);}putchar(x%10+'0');}
//快读,快写,利用getchar()与putchar()比cin,cout快的特性
char p1 , p2;
int l1 , l2;
char str1[MAXN] , str2[MAXN];
int k[MAXN*2];

inline int solve(){
	memset(k , 0 , sizeof(k));//初始化数组
	int ans = 0;//答案
	register int x = 0;//以下是kmp的模版,比常规暴力时间更少
	for(register int i = 2 ; str2[i] ; ++i){
		while(x && str2[x+1] != str2[i])
			x = k[x];
		if(str2[x+1] == str2[i])
			++x;
		k[i] = x;
	}
	x = 0;
	for(register int i = 1 ; str1[i] ; ++i){
		while(x>0 && str2[x+1] != str1[i]){
			x = k[x];
		}
		if(str2[x+1] == str1[i]){
		}
		if(x == strlen(str2+1)){
			ans++;
			x = k[x];
		}
	}
	return ans;
}



int main(){
	
	freopen("oulipo.in" , "r" , stdin);
	freopen("oulipo.out" , "w" , stdout); 
	int n;
	n = read();
	//常规输入
	for(register int i = 1 ; i <= n ; ++i){
		memset(str1 , 0 , sizeof(str1));
		memset(str2 , 0 , sizeof(str2));
		scanf("%s" , str2+1);//下标从一开始
		scanf("%s" , str1+1);//格式化输入输出一定要学会。不喜欢的可以用cin更方便
		write(solve());
		putchar('\n');
	}
	
	return 0;
}

祝大家能顺利的通过每一场比赛!早日夺得noi金牌!加油!



题目1570  [POJ 3461] 乌力波 AAA      2      评论
2023-08-18 22:19:53    
Gravatar
op_组撒头屯
积分:2982
提交:327 / 662

跟下面那道题比起来属于是纯纯的小清新题了,这就是原友出题人和原批出题人的差距。不过当时的 FFT 应该没有现在这么普及,就像原神在最初几年也没有引起轩然大波一样。


这种组合数取模肯定考虑卢卡斯定理,况且 $n \lt p^{10}$ 也算是提醒你了。

将 $n$ 和 $k$ 写成 $p$ 进制数 $\overline{n_N...n_0}$ 和 $\overline{k_N...k_0}$,根据卢卡斯定理有 $C_n^k=\prod_i{C_{n_i}^{k_i}}$。显然每个 $k_i$ 的贡献是可以分开算的。


令 $f_{i,j}$ 表示前 $i$ 位组合数累乘后 $\%p=j$ 的方案数,$g_{i,j}$ 表示使得 $C_{n_i}^k \%p = j$ 的 $k$ 的数量,转移是简单的:

$$f_{i,j}=\sum_{(x\times y) \%p=j}{f_{i-1,x} g_{i,y}}$$

这转移式和卷积的相似度可以与原神媲美了,然而这个下标是相乘而不是相加。

但这玩意属于是典中典了,直接把下标对 $p$ 的原根取对数就可以转化成相加了,具体见 “[SDOI2015]序列统计”。


重新定义一下,令当前选择的原根是 $G$。

令 $f_{i,j}$ 表示前 $i$ 位组合数累乘后 $\%p=G^j$ 的方案数,$g_{i,j}$ 表示使得 $C_{n_i}^k \%p = G^j$ 的 $k$ 的数量,将转移写成和卷积:

$$F_i=\sum_{j\ge 0}{f_{i,j}x^j}\quad\quad G_i=\sum_{j\ge 0}{g_{i,j}x^j}$$

$$\Rightarrow F_{i}=\sum_{j\ge 0}(\sum_{(x+y)\%(p-1)=j}{f_{i-1,x} g_{i,y}})x^j=F_{i-1}G_i$$


大概就是这样,注意下标相加实际是指数相加,所以根据费马小定理对 $p-1$ 取模。以及这样卷积出来会有下标大于 $p-1$ 的项,及时合并到前面即可。

介于答案模数较小,使用 FFT 计算卷积,时间复杂度大概是 $O(10p\log(p))$。

勉强加入“超级无敌神仙炫酷无敌原神大王”豪华套餐。



题目1797  [国家集训队2012]binomial      10      评论
2023-08-05 18:39:20    
Gravatar
op_组撒头屯
积分:2982
提交:327 / 662

数个月前说要写,鸽到现在,菜。

属于是惊世骇俗的神秘题了。

拿到式子直接开始乱推:


$$ \sum_{i=1}^{n}{\gcd(i,n)^x \mathrm{lcm}(i,n)^y}\\ $$

$$   =\sum_{i=1}^{n}{i^yn^y\gcd(i,n)^{x-y}}\\ $$

$$   =n^y\sum_{d|n}{d^{x-y}\sum_{i=1}^{n}{i^y[\gcd(i,n)=d]}}\\ $$

$$   =n^y\sum_{d|n}{d^{x-y}\sum_{i=1}^{\frac{n}{d}}{(id)^y[\gcd(i,\frac{n}{d})=1]}}\\ $$

$$   =n^y\sum_{d|n}{d^x\sum_{i=1}^{\frac{n}{d}}{i^y\sum_{k|i,k|\frac{n}{d}}{\mu(k)}}}\\ $$

$$   =n^y\sum_{d|n}{d^x\sum_{k|\frac{n}{d}}{\mu(k)\sum_{i=1}^{\frac{n}{dk}}{(ik)^y}}}\\ $$

$$   =n^y\sum_{d|n}{d^x\sum_{k|\frac{n}{d}}{\mu(k)k^y\sum_{i=1}^{\frac{n}{dk}}{i^y}}}\\ $$

$$   =n^y\sum_{d|n}{d^x\sum_{k|\frac{n}{d}}{\mu(k)k^yS(\frac{n}{dk},y)}}\\ $$



玩原神的人都知道 $S(\frac{n}{dk},y)$ 是个 $y+1$ 次多项式,这就是原神带给我骄傲的资本。记 $S=\sum_{i=0}^{y+1}{f_ix^i}$ 代入:


$$ =n^y\sum_{d|n}{d^x\sum_{k|\frac{n}{d}}{\mu(k)k^y\sum_{i=0}^{y+1}{f_i(\frac{n}{dk})^i}}}\\$$

$$  =n^y\sum_{i=0}^{y+1}{f_i\sum_{d|n}{d^x\sum_{k|\frac{n}{d}}{\mu(k)k^y{(\frac{n}{dk})^i}}}}\\$$

$$  =n^y\sum_{i=0}^{y+1}{f_i(\mu \cdot Id^y \ast Id^x \ast Id^i)_{(n)}}\\$$


三个函数卷积,纯纯的卷狗。

好在三个积性函数卷出来也是积性函数,于是 pollard_rho 出 $n$ 的所有质因子分开算。


$$(\mu \cdot Id^y \ast Id^x \ast Id^i)_{(p^c)}\\$$

$$  =\sum_{j=0}^{c}{\mu(p^j)p^{yj}\sum_{k=0}^{c-j}{p^{kx}p^{(c-j-k)i}}}\\$$

$$  =\sum_{k=0}^{c}{p^{ci+(x-i)k}}-p^y\sum_{k=0}^{c-1}{p^{(c-1)i+(x-i)k}}\\$$


两坨都是等比数列求和,好好好,玩原神玩的。

最后就是这个 $f_i$。

如果你玩原神,那么你就会伯努利数,那么就知道:


$$\sum_{i=1}^{n}{i^k}=\frac{1}{k+1}\sum_{j=0}^{k}{(-1)^jC_{k+1}^jB_jn^{k+1-j}}$$


其中 $B_j$ 是伯努利数,可以用 EGF 求:


$$B(x)=\sum_{i \ge 0}{B_i\frac{x^i}{i!}}=\frac{x}{e^x-1}$$


然后就结束了。原神,启动!

听说还很卡常,已加入“超级无敌神仙炫酷无敌原神大王”豪华套餐。



题目1770  [国家集训队2012]JZPKIL      9      评论
2023-08-01 21:43:41