一
卷积
(convolution.pas/c/cpp)
【问题描述】
我们先介绍 Dirichlet 卷积。
在 算术函数集 上,可以定义一种二元运算,使得取这种运算为乘法,取普通
函数加法为加法,使得 算术函数集 为一个交换环。其中一种这样的运算便是
Dirichlet 卷积。它和一般的卷积有不少相类之处。
对于算术函数
f,g
f
,
g
,定义其 Dirichlet 卷积
Dirichlet 卷积有很多美妙的性质:
1. 取 Dirichlet 卷积为运算,积性函数集是算术函数集的 子群。
2. 交换律 f∗g=g∗f f ∗ g = g ∗ f
3. 结合律 (f∗g)∗h=f∗(g∗h) ( f ∗ g ) ∗ h = f ∗ ( g ∗ h )
4. 分配律 (f+g)∗h=f∗g+f∗h=h∗(f+g) ( f + g ) ∗ h = f ∗ g + f ∗ h = h ∗ ( f + g )
5. 存在 单位函数ε 使得 f∗ε=ε∗f f ∗ ε = ε ∗ f 。 ε(n)的值为 1 若 n=1 n = 1 ,否则 ε(n)=0 ε ( n ) = 0 。
6. 对于任意算术函数 f f ,若 不等于 0 0 ,都有唯一的 逆函数 ,使得
f∗f−1=ε f ∗ f − 1 = ε 。 莫比乌斯函数 μ μ 的 逆函数 为(一般意义上的) 1,即对于
这是 莫比乌斯反转公式的原理。
这些性质使得 Dirichlet 卷积 在数论中有着特别重要的地位。
现在定义一种运算 有序序列的 Dirichlet 卷积 K K 次自乘,假设原序列为,结
果序列为 g g ,则有:
K K 个
现在给出 f f ,我们需要知道的 Dirichlet 卷积 K K 次自乘。
【输入】
输入文件名为 convolution .in,共 2 行。
第一行包含两个正整数。
下面一行
N
N
个非负整数,第个数表示
fi
f
i
。
【输出】
输出文件名为 convolution .out,共一行,第 i i 个数表示。
【输入输出样例】
输入:
5 2
50 56 85 43 60
输出:
2500 5600 8500 7436 6000
【数据说明】
对于 30%的数据满足, K=1 K = 1 ;
对于100%的数据满足, 1≤N≤1e4,1≤K≤10,0≤fi≤1e5 1 ≤ N ≤ 1 e 4 , 1 ≤ K ≤ 10 , 0 ≤ f i ≤ 1 e 5
题目大意
要是我能总结看懂题目大意,我这题还用骗分
思路
模拟
这可能是我写过的最短的题解了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 1000000009
#define cl(x) memset(x,0,sizeof(x))
#define rg register
template <typename _Tp> inline void read(_Tp &x){char c11=getchar();x=0;bool booo=0;
while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
if(booo)x=-x;return ;
}
const int maxn=100020;
ll n,k,f[maxn],d[maxn],now[maxn];
void init();
void work(){
for(rg int i=1;i<k;++i){
cl(now);
for(rg int j=1;j<=n;++j)
for(rg int l=1;l*j<=n;++l)
now[l*j]=(now[l*j]+d[j]*f[l])%mod;
for(rg int j=1;j<=n;++j)d[j]=now[j];
}
for(rg int i=1;i<=n;++i)printf("%lld ",d[i]);
}
int main(){
freopen("convolution.in","r",stdin);
freopen("convolution.out","w",stdout);
init();
work();
return 0;
}
void init(){
read(n);read(k);
for(rg int i=1;i<=n;++i)read(f[i]),d[i]=f[i];
}
二
巡访
(path.pas/c/cpp)
【问题描述】
Chanxer 终于当上了“中华农民联盟”的盟主,他举目四望,决定四处走走,
巡视自己的农土。
“中华农民联盟”的成员有N个村庄,在“村村通”计划中,村庄们被N − 1条
道路联通了起来,Chanxer 计划从某个村庄出发,访问所有的村庄。
可是 Chanxer 出行有一个特殊的要求,那就是必须以农车代步,现在我们知
道哪些村庄配备有农车,也就是说,只有配备有农车的村庄才能够被作为出发点。
Chanxer 有点懒,他想知道访问全部的村庄所要走的路程长度最小是多少。
【输入】
输入文件名为 path.in,共
N+1
N
+
1
行,第一行包含一个正整数
N
N
。
接下来行,每行包含三个个整数
xi
x
i
,
yi
y
i
,
zi
z
i
,表示
xi
x
i
号村庄和
yi
y
i
号村庄之
间有一条长度为
zi
z
i
的路。
接下来一行包含
N
N
个数,每个数不是 就是
1
1
,若第个数为
1
1
表示可以选择
该点为起始点,否则不可以选。
【输出】
输出文件名为 path.out,共一行,包含一个非负整数,表示最小的路程长度,
若无法访问全部村庄则输出。
【输入输出样例】
输入
5
1 2 1
1 3 1
3 4 3
3 5 4
1 1 1 0 0
输出
12
【数据说明】
6个测试点的数据满足,
N≤10
N
≤
10
;
100%的数据满足,
N≤1e5,1≤xi,yi≤N,1≤zi≤1e4
N
≤
1
e
5
,
1
≤
x
i
,
y
i
≤
N
,
1
≤
z
i
≤
1
e
4
。
题目概要
给定一棵边有权值的树,求从给定允许的点中选一点开始遍历,求最小路径
思路
6分解法
puts(“-1”);
24分解法
一看便知树型Dp, g[i] g [ i ] , f[i] f [ i ] 分别表示从 i i 这儿出发遍历子树不回来与回来的最小权值数,做n次Dp
100分做法
感性地想一想,一条边最多走两次:去与来
那么我们考虑答案的补集,就是有哪些边不用走两次,由于要遍历一棵树,必须遍历所有边,所以答案转化为求哪些边只走一遍
由于路线必须是连续的(也就是一条链),所以我们又把题目转化为求树上最长链
为什么是最长链呢?
因为要求路径最小,而全集一定,所以要求补集最大,也就是树上最长链
24分树型Dp解法程序
#include<bits/stdc++.h>
using namespace std;
#define inf 2000000050
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;
while(c11<'0'||c11>'9')c11=getchar();
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}return ;
}
const int maxn=100050,maxm=200050;
struct node {int v,w,nxt;} a[maxm];
int head[maxn],p=0;
bool bo[maxn];
int f[maxn],g[maxn];
int ans=inf;
int n;
inline void add(int,int,int);
void init();
void dfs(int x,int las){
f[x]=0;
for(rg int i=head[x];i;i=a[i].nxt)if(a[i].v!=las){
int v=a[i].v,w=a[i].w;
dfs(v,x);
f[x]+=w*2+f[v];
}
g[x]=f[x];
int maxx=0;
for(rg int i=head[x];i;i=a[i].nxt)if(a[i].v!=las)
maxx=max(maxx,a[i].w+f[a[i].v]-g[a[i].v]);
g[x]-=maxx;
}
void work(){
for(rg int i=1;i<=n;++i)
if(bo[i]){
dfs(i,0);
ans=min(ans,g[i]);
}
if(ans!=inf)
printf("%d\n",ans);
else
printf("-1\n");
return ;
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
init();
work();
return 0;
}
void init(){
read(n);
int A,B,C;
for(rg int i=1;i<n;++i){
read(A);read(B);read(C);
add(A,B,C);add(B,A,C);
}
for(rg int i=1;i<=n;++i)read(bo[i]);
}
inline void add(int u,int v,int w){
a[++p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p;
}
三
大炮
(noligon.pas/c/cpp)
【问题描述】
“农气大炮”是 Chanxer 毕生精力凝结而成的心血。“当大炮建成时,普天之下,莫非农土!”,现在 Chanxer 还有最后一步,就是为大炮装载农气续航系统,换句话说,就是上电池。Chanxer 现在有N条能量棒,第 条的长度为
2ki
2
k
i
,且拥有一个不稳定值
Wi
W
i
。为了避免因为单次高强度攻击而导致大炮瘫痪,Chanxer 觉得采用能源分离装置,他准备了许多能量槽,每个能量槽都是条形的,横截面积恰好容得下一根
能量棒插入,而能量槽的深度也为
2t
2
t
的形式。现在,Chanxer 需要再能量棒中选择一些插入能量槽中,为了保证续航时间的最大化,Chanxer 要求每个能量槽都必须完全装满,也就是说,插入其中的能量棒的最长度要正好等于能量槽的深度,与此同时,Chanxer 希望被选择的能量棒的总不稳定值最小。
【输入】
输入文件名为 noligon.in,共
N+M+2
N
+
M
+
2
行。
第一行包含一个整数
N
N
。
接下来 行,每行两个整数
ki,Wi
k
i
,
W
i
。
接下来一行包含一个整数
M
M
。
接下来行,每行两个数
t
t
,,表示深度为
2t
2
t
的能量槽有
h
h
个。
【输出】
输出文件名为 noligon.out,共一行。
输出一个数表示最小的总不稳定值,假如不存在解就输出。
【输入输出样例】
输入
5
1 3
1 2
3 5
2 1
1 4
2
1 1
2 1
输出
3
【数据说明】
对于30%的数据满足,
1≤n,m≤12
1
≤
n
,
m
≤
12
;
对于100%的数据满足,
1≤n≤10000,0≤ki≤1,000,0≤Wi≤10,000,0≤t≤1000,h≥0,∑h≤5000
1
≤
n
≤
10000
,
0
≤
k
i
≤
1
,
000
,
0
≤
W
i
≤
10
,
000
,
0
≤
t
≤
1000
,
h
≥
0
,
∑
h
≤
5000
。
题目概要
给定n件物品,m个格子(空间是 2k 2 k 的形式),求填满每个格子的最小花费
思路
10分的’-1’骗分就不说了
直接说正解, 发现它的空间很有特色,是 2k 2 k 的形式,我们凭借幼儿园奥数知识可知,
接下来开始
首先,需知道有没有解:
为了方便描述,记
Numi(i>=0)
N
u
m
i
(
i
>=
0
)
为尺寸为I的盒子个数;
Ti(i>=0)
T
i
(
i
>=
0
)
为尺寸为I的集装箱个数;
①由于尺寸为0(大小
20
2
0
)的集装箱必须由尺寸为0的盒子来填,所以,如果Num[0]比T[0]小则必定无解,否则能把尺寸为0的集装箱的填满;
②由于尺寸为1(大小21)的集装箱必须由尺寸为0或1的盒子来填,并且只能包含偶数个尺寸为0的组成,而尺寸为0的盒子还剩下Num[0]-T[0]个,这些盒子显然可以两两组合成(Num[0]-T[0])DIV 2个尺寸为1的盒子,故可看作总共有(Num[1]+(Num[0]-T[0])DIV 2)个尺寸为1的盒子,所以Num[1]+ Num[1]+(Num[0]-T[0])DIV 2,Num[0]+0,在按与第一步类似的方法处理即可;
③以此类推;
④若尺寸为0~1000的集装箱都可填满,则必定有解。
然而,增么得到最优解呢?
实际上,只需在求可行解时多加一些运算便可以保证可行解为最优了。
即:1、第k步时,若非无解情况,则选前T[k-1]个价值最小的盒子;
2、第k步时,剩下Num[k-1]-T[k-1]个盒子,则把它从小到大排序后,让第2*I-1与2*I(I>=1)个盒子结合成一个尺寸为k的大盒子。
这样是可以得到最优值。
证明:若存在可行解,而在某一步k中,选的不是前T[k-1]个价值最小的盒子;我们不妨设取了一个价值为b的尺寸为k-1的盒子,且b不是前T[k-1]小的,则必定有一个(或更多)前T[k-1]小的没取,设它的价值为a,显然有b>=a,那我们将原方案中这两个盒子的位子交换一下(若交换前价值为a的这个没有被选,则交换后不选价值为b的这个盒子),就可能得到更优的解(至少不会差),所以,每次都要选价值最小的前几个,而第2步也不难明白:这样组合能使得到的Num[k-1]-T[k-1]DIV 2个盒子的前j(1<=j<=Num[k-1]-T[k-1]DIV 2)小的价值和达到最小,从而使后面的选择也正确。
执行步骤:
1. 读入数据;
2. 分别对不同尺寸的盒子按升序排序,存在value[I]数组中(value[I]中按升序保存所有尺寸为I的盒子的价值);
3. K0,Min0;
4. 若Num[k] < T[k]则转9;
5. 选出value[k]中1到T[k]个盒子,MinMin+value[k,1]+value[k,2] +…value[k,T[k]];
6. 若k<1000,则将剩下的T[k]-Num[k]个盒子,两两组合成(T[k]-Num[k])DIV 2个尺寸为k+1的{第T[k]+1与T[k]+2,T[k]+3与T[k]+4…,组合},在此过程中,把新生成的(T[k]-Num[k])DIV 2归并入value[k+1]中,且Num[k+1]Num[k+1]+ (T[k]-Num[k])DIV 2
7. K:=K+1,若k<=1000转4否则输出Min;
8. 结束
9. 输出无解,转8
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define cl(x) memset(x,0,sizeof(x))
#define rg register
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;
while(c11<'0'||c11>'9')c11=getchar();while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
return ;
}
const int maxn=10050;
int m[maxn];
int n,ans=0;
priority_queue <ll,vector<ll>,greater<ll> > q[1500];
int maxl=0,M;
void init();
void work(){
for(rg int i=0;i<=maxl;++i){
while(m[i]){
if(q[i].empty()){printf("-1");return ;}
ans+=q[i].top();q[i].pop();
--m[i];
}
int temp;
while(!q[i].empty()){
temp=q[i].top();q[i].pop();
if(q[i].empty())break;
q[i+1].push(temp+q[i].top());
q[i].pop();
}
}
printf("%d\n",ans);
}
int main(){
freopen("noligon.in","r",stdin);
freopen("noligon.out","w",stdout);
init();
work();
return 0;
}
void init(){
read(n);
cl(m);
int A,B;
for(rg int i=1;i<=n;++i){
read(A);read(B);
q[A].push(B);
}
read(M);
for(rg int i=1;i<=M;++i){
read(A);read(B);
m[A]+=B;
if(maxl<A)maxl=A;
}
}