2019-9-24
A:链
挺好玩的,做不出来也有一点无奈
如果有个点u在某个时刻deg为3,v1,v2,v3
那么答案一定只能在u,v1,v2,v3中
于是维护这四个点的答案就好
当所有点度数小于等于2的时候,随便讨论一下就好了
B:子集和
一个好的突破点是:给出的子集和是若干个形如
1
+
x
y
1+x^y
1+xy的乘积
把0去掉
那么对于当前多项式(然后这个多项式的指数可以是负数…)
的次小指数-最小指数就是当前绝对值最小的y的绝对值
你发现
1
+
x
y
=
x
y
(
1
+
x
−
y
)
1+x^y=x^y(1+x^{-y})
1+xy=xy(1+x−y)
所以这些乘起来只有一个位移的区别
excellent!
需要乘起来的最大指数和原多项式的最大指数一样
这样的情况下
绝对值大的定好尽量为负
这样的情况下,简单的dp+贪心即可
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=1e18;
int n;
#define Maxn 10010
ll all,maxv,to;
int num0;int S;
ll s[Maxn],p[Maxn];
struct P{
ll rat,at;
bool operator <(const P &z)const{return at>z.at;}
}A[Maxn],B[Maxn],C[Maxn],D[Maxn];
int len;
ll val[Maxn];int cnt;
bool ok[Maxn];
bool dp[62][Maxn];
void Divide(ll y){
val[++cnt]=y;
int L=0,at1=1,at2=1,at3=0;
while(at1<=len||at2<=L){
ll tmp=-inf;
if(at1<=len)tmp=max(tmp,A[at1].at);
if(at2<=L)tmp=max(tmp,B[at2].at);
ll s=0;
if(at1<=len&&tmp==A[at1].at)s+=A[at1++].rat;
if(at2<=L&&tmp==B[at2].at)s+=B[at2++].rat;
if(!s)continue;
C[++at3]=(P){s,tmp};
B[++L]=(P){-s,tmp-y};
}
len=at3;
for(register int i=1;i<=len;++i)A[i]=C[i];
}
void Mul(ll y){
for(int i=1;i<=len;++i){
B[i]=A[i];
C[i]=(P){A[i].rat,A[i].at+y};
}
int pre=len;
int hd1=1,hd2=1;
len=0;
while(hd1<=pre&&hd2<=pre){
ll tmp=max(B[hd1].at,C[hd2].at),s=0;
if(B[hd1].at==tmp)s+=B[hd1++].rat;
if(C[hd2].at==tmp)s+=C[hd2++].rat;
A[++len]=(P){s,tmp};
}
for(int i=hd1;i<=pre;++i)A[++len]=B[i];
for(int i=hd2;i<=pre;++i)A[++len]=C[i];
}
ll ans[Maxn];
inline void rd(int &x){
x=0;char ch=getchar();int f=1;
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
inline void rd(ll &x){
x=0;char ch=getchar();int f=1;
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
int main(){
int T;
rd(T);
for(register int tt=1;tt<=T;++tt){
rd(n);
all=0;len=n;
num0=0;cnt=0;
maxv=-inf;to;
for(register int i=1;i<=n;++i)rd(A[i].at);
for(register int i=1;i<=n;++i){
rd(A[i].rat);
D[i]=A[i];
all+=A[i].rat;
if(A[i].at>=maxv){
maxv=A[i].at;
to=A[i].rat;
}
}
sort(A+1,A+n+1);
sort(D+1,D+n+1);
while(to%2==0){to/=2;num0++;}
S=0;
for(int i=1;i<=n;++i)A[i].rat/=(1ll<<num0);
while(all%2==0){
all/=2;
S++;
}
S-=num0;
cnt=0;
for(register int i=1;i<=S;++i)
Divide(A[len-1].at-A[len].at);
sort(val+1,val+S+1);
len=1;
memset(ok,false,sizeof(bool)*(S+1));
A[1].at=0;A[1].rat=1;
ll sum=0;
for(register int i=1;i<=cnt;++i)sum+=val[i];
for(register int i=1;i<=n;++i)D[i].at+=sum-maxv;
int zz;
for(register int i=1;i<=n;++i){
if(!D[i].at)dp[0][i]=true;
else dp[0][i]=false;
if(D[i].at==maxv)zz=i;
}
int At;
if(dp[0][zz])At=0;
else{
for(register int i=1;i<=S;++i){
int haha=1;
for(register int j=1;j<=n;++j){
dp[i][j]=dp[i-1][j];
while(haha<=n&&D[j].at-val[i]<D[haha].at)haha++;
if(haha<=n&&D[haha].at==D[j].at-val[i])dp[i][j]|=dp[i-1][haha];
}
if(dp[i][zz]){
At=i;
break;
}
}
}
int d=zz;
while(D[d].at&&d<=n){
ll tmp=D[d].at-val[At];ok[At]=true;
while(tmp<D[d].at)d++;
At--;
while(At&&dp[At-1][d])At--;
}
for(register int i=1;i<=S;++i)
if(ok[i])ans[i]=val[i];
else ans[i]=-val[i];
for(register int i=1;i<=num0;++i)ans[S+i]=0;
sort(ans+1,ans+S+num0+1);
printf("Case #%d:",tt);
for(register int i=1;i<=S+num0;++i)printf(" %lld",ans[i]);
puts("");
}
return 0;
}/*
3
8
0 1 2 3 4 5 6 7
1 1 1 1 1 1 1 1
4
0 1 3 4
4 4 4 4
5
-2 -1 0 1 2
1 2 2 2 1
*/
2019-9-26
Life is like a box of chocolates
C题裸的大数据结构题,不想说了
A:有趣的期望
每次更新若不是当前最小值,那么新的最小值可以很轻易的得出
否则暴力重建
注意,当前有多个最小值取的数都不算最小值
否则概率不是
1
n
\frac{1}{n}
n1
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned int uint;
const uint trans=10099;
#define Maxn 10000010
uint num[Maxn],x[Maxn<<1];
uint n,m,a,b,c;
int main(){
scanf("%u%u%u%u%u%u%u",&n,&m,&x[0],&x[1],&a,&b,&c);
for(register int i=2;i<=2*m;++i)x[i]=(a*x[i-2]+b*x[i-1]+c);
for(register int i=0;i<n;++i)num[i]=(1ll<<32)-1;
uint minv=(1ll<<32)-1,all=n,res=10099,ans=0;
for(register int i=1;i<=m;++i){
uint tmp1=(x[i*2-1]>>2)%n;
uint tmp2=(x[i*2]>>2);
if(num[tmp1]!=minv){
if(tmp2<minv){
minv=tmp2;
all=1;
}else if(tmp2==minv){
all++;
}
num[tmp1]=tmp2;
}else{
if(all>1){
all--;
num[tmp1]=tmp2;
if(tmp2<minv){
minv=tmp2;
all=1;
}else if(tmp2==minv)all++;
}else{
num[tmp1]=tmp2;
minv=(1ll<<32)-1;
for(register int j=0;j<n;++j)minv=min(minv,num[j]);
all=0;
for(register int j=0;j<n;++j)
if(num[j]==minv)all++;
}
}
ans+=minv*res;
res*=trans;
}
printf("%u\n",ans);
return 0;
}
B:啦啦啦
先删除,再更改,再移位,再加入
好啦,dp啦,怎么dp
s的前i对t的前j
操作1:f[i][j]=f[i][j-1]+a
操作2:f[i][j]=f[i-1][j]+b
操作3:f[i][j]=f[i-1][j-1]+c
由于a+b<=2*d,因此每个数只会交换一次,并且交换后不会替换。(交换后替换跟两个替换比较得交换小于等于替换,而用
a
+
b
<
=
2
∗
d
<
=
d
+
c
a+b<=2*d<=d+c
a+b<=2∗d<=d+c)
操作4:记k为s中上一个t[j]的位置,l为t中上一个s[i]的位置,f[i][j]=f[k-1][l-1]+d+(i-k-1)*b+(j-l-1)*a
这样选k,l最好,别的k和l可以尝试反证
时间复杂度
O
(
∣
s
∣
∣
t
∣
)
O(|s||t|)
O(∣s∣∣t∣)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define Maxn 4005
char S[Maxn],T[Maxn];
int last1[26],last2[26],len1,len2;
int f[Maxn][Maxn],a,b,c,d;
int main(){
scanf("%d%d%d%d",&a,&b,&c,&d);
scanf("%s%s",S+1,T+1);
len1=strlen(S+1);
len2=strlen(T+1);
for(register int i=1;i<=len2;++i)f[0][i]=a*i;
f[0][0]=0;
for(register int i=1;i<=len1;++i){
memset(last2,0,sizeof(last2));
f[i][0]=b*i;
for(register int j=1;j<=len2;++j){
f[i][j]=400000000;
f[i][j]=min(f[i][j],f[i][j-1]+a);
f[i][j]=min(f[i][j],f[i-1][j]+b);
f[i][j]=min(f[i-1][j-1]+c,f[i][j]);
if(S[i]==T[j])f[i][j]=min(f[i][j],f[i-1][j-1]);
int k=last1[T[j]-'a'],l=last2[S[i]-'a'];
if(k&&l)f[i][j]=min(f[i][j],f[k-1][l-1]+d+(i-k-1)*b+(j-l-1)*a);
last2[T[j]-'a']=j;
}
last1[S[i]-'a']=i;
}
printf("%d\n",f[len1][len2]);
return 0;
}
2019-9-27
A:给你一个质数P,C
给你递推式子
S_i=
a
S
i
−
2
+
b
S
i
−
1
+
c
aS_{i-2}+bS_{i-1}+c
aSi−2+bSi−1+c的形式
问你n项内有多少个摸P同余C
a=0特判
考虑矩阵乘法
否则你发现这个矩阵可逆
于是像BSGS那样处理出
P
P
P\sqrt P
PP个位置的矩阵
乘上
0
P
−
1
0~\sqrt P-1
0 P−1次方加哈希查找相同
注意到,因为可逆,所以周期一定包含第一个
B:线段树要敢想敢写
2019-9-29
冒泡排序从01序列角度考虑会有奇特效果
C:若有m种货币,找出所有可以组成的价值,且任意两个货币价值的积小于等于10000
最短路建图,货币价值c从小到大排序,在模c1的情况下求最短路,长度不超过10000
这个可以解释noip 2017 day1t1
2019-9-30
从FWT的逆变换角度思考有奇迹
同时如果一道题操作次数较少可以考虑每次一次性进行前面的修改
xor的FWT
F
W
T
(
A
0
+
A
1
)
=
F
W
T
(
A
0
)
+
F
W
T
(
A
1
)
,
F
W
T
(
A
0
)
−
F
W
T
(
A
0
)
−
F
W
T
(
A
1
FWT(A_0+A_1)=FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_0)-FWT(A_1
FWT(A0+A1)=FWT(A0)+FWT(A1),FWT(A0)−FWT(A0)−FWT(A1
2019-10-1
判断第一步可以走哪
考虑一个思路
如果会判断胜负
那么枚举第一步,看看下面能否后手胜
2019-10-3
学会了一个求treap期望高度的方法
设
h
i
h_i
hi表示i个点的笛卡尔树每个点高度和的期望
则
f
i
+
1
=
1
+
f
i
i
+
1
+
i
i
+
1
f_{i+1}=1+\frac{f_i}{i+1}+\frac{i}{i+1}
fi+1=1+i+1fi+i+1i
那么
f
i
=
i
l
o
g
2
i
f_i=ilog_2i
fi=ilog2i
那么这启发我们一个好玩的东西
所有点的高度和除以点的个数就是子树大小的期望
没错就是
l
o
g
2
i
log_2i
log2i
发现旋转treap似乎也可以持久化
2019-10-5
然而我写的比较挫,没有除以a
一些形如FFT的式子,可以考虑用dp求其DFT
在DFT的层面上进行运算
然后再IDFT
一些动态dp中
矩阵的更改中若n*n的行列置换一样
那么可以很快地得到更改后的乘积
2019-10-8
A题:agc004-e
额
利用模的性质可以把
f
i
f_i
fi表示成
l
o
g
m
log_m
logm个和
然后后面的就可以很好地处理了
C题:AGC 004 F Namori
神题!
2019-10-9
A题,
有时候树形dp的过程可以用直径简化