定义
对10%的数据, 1≤n,m≤100
对30%的数据, 1≤n,m≤1000
对另外30%的数据, T≤3
对100%的数据, T≤1000,1≤n,m≤1000000
O(n2) 枚举,现算gcd和f(强行 O(n3)
预处理f和gcd, O(n2)
考虑反演
首先题目要求的那个式子直接可以化成
令 T=dt ,我们放弃枚举d,改成枚举T,也就是由枚举倍数变成枚举约数,那么现在d是T的约数
这道题当时考试的时候只做出来了60分,并且调试的时间也稍微有点长。主要让我困惑的是连加和连乘放在了一起就没办法搞出来一个科学的卷积或者相似的形式了。其实考试之前花了很长一段时间练习反演是见到了很多枚举约数改成枚举倍数的题目的,但是感觉当时没有认真想,以前的题目全部都是连加好像怎么都是对的,于是一变形式就不会了。实际上再往下画一画是不难的。
所以以后再思维方面是绝不能偷懒了。
60pts
#include
#include
#include
#include
#include
using namespace std;
#define Mod 1000000007
#define phi 1000000006
#define N 1000005
#define LL long long
int T,n,m;
LL ans;
int p[N],prime[N];
LL mu[N],f[N];
void get(int n)
{
f[0]=0;f[1]=1;
for (int i=2;i<=n;++i) f[i]=(f[i-1]+f[i-2])%Mod;
for (int i=2;i<=n;++i) f[i]=(f[i]*f[i-1])%Mod;
mu[1]=1;
for (int i=2;i<=n;++i)
{
if (!p[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
{
p[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
for (int i=1;i<=n;++i) mu[i]=(mu[i]+mu[i-1])%phi;
}
LL fast_pow(LL a,LL p,LL mod)
{
LL ans=1;
for (;p;p>>=1LL,a=a*a%mod)
if (p&1)
ans=ans*a%mod;
return ans;
}
LL sum(int n,int m)
{
LL ans=0;
if (n>m) swap(n,m);
for (int i=1,j=0;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans=(ans+(LL)(n/i)*(m/i)%phi*(mu[j]-mu[i-1])%phi)%phi;
}
ans=(ans+phi)%phi;
return ans;
}
int main()
{
freopen("product.in","r",stdin);
freopen("product.out","w",stdout);
get(1000000);
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
if (n>m) swap(n,m);
ans=1;
for (int i=1,j=0;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
LL now=f[j];
if (i!=1) now=now*fast_pow(f[i-1],Mod-2,Mod)%Mod;
LL s=sum(n/i,m/i);
now=fast_pow(now,s,Mod);
ans=(ans*now)%Mod;
}
ans=(ans+Mod)%Mod;
printf("%I64d\n",ans);
}
}
埃氏筛法
#include
#include
#include
#include
#include
using namespace std;
#define Mod 1000000007
#define phi 1000000006
#define LL long long
#define N 1000005
int T,n,m;
int p[N],prime[N],mu[N];
LL ans;
LL f[N],g[N],inv[N];
LL fast_pow(LL a,int p)
{
LL ans=1;
for (;p;p>>=1,a=a*a%Mod)
if (p&1)
ans=ans*a%Mod;
return ans;
}
void get(int n)
{
f[0]=0;f[1]=1;
for (int i=2;i<=n;++i) f[i]=(f[i-1]+f[i-2])%Mod;
mu[1]=1;
for (int i=2;i<=n;++i)
{
if (!p[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
{
p[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
for (int i=1;i<=n;++i) g[i]=inv[i]=1;
for (int i=1;i<=n;++i)
for (int j=i;j<=n;j+=i)
{
int t=j/i;
if (mu[t]==1) g[j]=g[j]*f[i]%Mod;
else if (mu[t]==-1) inv[j]=inv[j]*f[i]%Mod;
}
for (int i=1;i<=n;++i) g[i]=g[i]*fast_pow(inv[i],Mod-2)%Mod;
for (int i=2;i<=n;++i) g[i]=g[i]*g[i-1]%Mod;
}
int main()
{
freopen("product.in","r",stdin);
freopen("product.out","w",stdout);
get(1000000);
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
if (n>m) swap(n,m);
ans=1;
for (int i=1,j=0;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
LL a=(LL)(n/i)*(m/i)%phi;
LL now=g[j];
if (i!=1) now=now*fast_pow(g[i-1],Mod-2)%Mod;
ans=ans*fast_pow(now,(int)a)%Mod;
}
printf("%I64d\n",ans);
}
}
暴力分解质因数
#include
#include
#include
#include
#include
using namespace std;
#define Mod 1000000007
#define phi 1000000006
#define LL long long
#define N 1000005
int T,n,m;
int p[N],prime[N],mu[N],d[1<<8];
LL ans;
LL f[N],g[N],inv[N];
LL fast_pow(LL a,int p)
{
LL ans=1;
for (;p;p>>=1,a=a*a%Mod)
if (p&1)
ans=ans*a%Mod;
return ans;
}
void get(int n)
{
f[0]=0;f[1]=1;
for (int i=2;i<=n;++i) f[i]=(f[i-1]+f[i-2])%Mod;
mu[1]=1;
for (int i=2;i<=n;++i)
{
if (!p[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
{
p[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
for (int T=1;T<=n;++T)
{
g[T]=inv[T]=1;
int totp=0;memset(d,0,sizeof(d));
int x=T;
for (int i=1;i<=prime[0]&&prime[i]*prime[i]<=x&&x>1;++i)
if (x%prime[i]==0)
{
++totp,d[1<<(totp-1)]=prime[i];
while (x%prime[i]==0) x/=prime[i];
}
if (x>1) ++totp,d[1<<(totp-1)]=x;
for (int i=0;i<1<if (i) d[i]=d[i^(i&-i)]*d[i&-i];
else d[i]=1;
int t=T/d[i];
if (mu[d[i]]==1) g[T]=g[T]*f[t]%Mod;
else if (mu[d[i]]==-1) inv[T]=inv[T]*f[t]%Mod;
}
}
for (int i=1;i<=n;++i) g[i]=g[i]*fast_pow(inv[i],Mod-2)%Mod;
for (int i=2;i<=n;++i) g[i]=g[i]*g[i-1]%Mod;
}
int main()
{
freopen("product.in","r",stdin);
freopen("product.out","w",stdout);
get(1000000);
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
if (n>m) swap(n,m);
ans=1;
for (int i=1,j=0;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
LL a=(LL)(n/i)*(m/i)%phi;
LL now=g[j];
if (i!=1) now=now*fast_pow(g[i-1],Mod-2)%Mod;
ans=ans*fast_pow(now,(int)a)%Mod;
}
printf("%I64d\n",ans);
}
}
orz ATP
#include
#include
#include
#include
#include
using namespace std;
#define Mod 1000000007
#define phi 1000000006
#define LL long long
#define N 1000005
int T,n,m;
int p[N],prime[N],mu[N],e[N],h[N],d[1<<8];
LL ans;
LL f[N],g[N],inv[N];
LL fast_pow(LL a,int p)
{
LL ans=1;
for (;p;p>>=1,a=a*a%Mod)
if (p&1)
ans=ans*a%Mod;
return ans;
}
void get(int n)
{
f[0]=0;f[1]=1;
for (int i=2;i<=n;++i) f[i]=(f[i-1]+f[i-2])%Mod;
mu[1]=1;
for (int i=2;i<=n;++i)
{
if (!p[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
e[i]=i;
h[i]=1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
{
p[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
e[i*prime[j]]=e[i];
h[i*prime[j]]=h[i];
break;
}
else
{
mu[i*prime[j]]=-mu[i];
e[i*prime[j]]=prime[j];
h[i*prime[j]]=i;
}
}
}
for (int T=1;T<=n;++T)
{
g[T]=inv[T]=1;
int totp=0;memset(d,0,sizeof(d));
int x=T;
while (x>1)
++totp,d[1<<(totp-1)]=e[x],x=h[x];
for (int i=0;i<1<if (i) d[i]=d[i^(i&-i)]*d[i&-i];
else d[i]=1;
int t=T/d[i];
if (mu[d[i]]==1) g[T]=g[T]*f[t]%Mod;
else if (mu[d[i]]==-1) inv[T]=inv[T]*f[t]%Mod;
}
}
for (int i=1;i<=n;++i) g[i]=g[i]*fast_pow(inv[i],Mod-2)%Mod;
for (int i=2;i<=n;++i) g[i]=g[i]*g[i-1]%Mod;
}
int main()
{
freopen("product.in","r",stdin);
freopen("product.out","w",stdout);
get(1000000);
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
if (n>m) swap(n,m);
ans=1;
for (int i=1,j=0;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
LL a=(LL)(n/i)*(m/i)%phi;
LL now=g[j];
if (i!=1) now=now*fast_pow(g[i-1],Mod-2)%Mod;
ans=ans*fast_pow(now,(int)a)%Mod;
}
printf("%I64d\n",ans);
}
}
一棵n个点并且以1号点为根的有根树,初始时每个点都有一种不同的颜色,定义一条路径的权值为路径上的颜色种类数。
有三个操作:
1 x
将x到根的路径上的所有点染上一种没有用过的新颜色
2 x y
询问树链xy的权值
3 x
在以x为根的子树中选择一个点,使这个点到根节点的路径权值最大,求最大权值
对于m个操作中的每一个2或3操作输出答案。
对10%的数据, 1≤n,m≤1000
对另外20%的数据,没有2操作
对另外20%的数据,没有3操作
对另外10%的数据,数据随机
对100%的数据, 1≤n,m≤100000
暴力暴力
每一次暴力修改暴力统计颜色数,修改之后要重新dfs
O(n2)
链剖统计区间颜色段数
数据随机不用谢手工栈hhh
这道题的关键是每一次都是将某一个点到根的路径上的点染一种没用过的新颜色
这个操作实际上和lct中的access操作很像,因为都是打通一条到根的路径
那么我们就可以将重边的贡献记为0,轻边的贡献记为1,每一个点的权记为根到当前点的边的贡献和,那么2操作实际上就是计算val(x)+val(y)-2*val(lca(x,y))+1,3操作实际上就是求以x为根的子树的权值最大值+1
那么就可以维护一个有根树的lct,然后在砍断一条重边的时候和连接一条重边的时候都对这条边相连的子树进行修改,这样就相当于是一个区间修改单点查询权值和一个区间修改区间查询最大值的问题,写两棵线段树就行了
这道题考试的时候没有什么科学的思路。。只打了10分暴力,还打跪了。。需要注意的是两个点暴力向上蹦的时候一定要注意像lca一样判断是否蹦到同一个点什么的,这个不能再出错了
这道题其实之前见过,是fye学姐暑假的一次胡策,当时没有做。。。感觉在考场上想出来不是不可能但是也是有难度的,看来以后见过的题一定要搞懂它,做过的题也要经常复习呀
#include
#include
#include
#include
#include
using namespace std;
#define N 100005
#define sz 17
int n,m,dfs_clock;
int tot,point[N],nxt[N*2],v[N*2];
int father[N],h[N],in[N],out[N],val[N],pt[N],f[N][sz+3];
int fa[N],ch[N][2];
int sum[N*4],maxn[N*4],delta[N*4];
//-----------------------------init------------------------------------
void add(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void dfs(int x,int fa)
{
father[x]=fa;h[x]=h[fa]+1;
in[x]=++dfs_clock;pt[dfs_clock]=x;
for (int i=1;i1]][i-1];
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
f[v[i]][0]=x;
dfs(v[i],x);
}
out[x]=dfs_clock;
}
//-----------------------------lca-------------------------------------
int lca(int x,int y)
{
if (h[x]int k=h[x]-h[y];
for (int i=0;iif (k>>i&1) x=f[x][i];
if (x==y) return x;
for (int i=sz-1;i>=0;--i)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
//----------------------------segtree----------------------------------
void update(int now)
{
sum[now]=sum[now<<1]+sum[now<<1|1];
maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
}
void pushdown(int now,int l,int r,int mid)
{
if (delta[now])
{
sum[now<<1]+=delta[now]*(mid-l+1);sum[now<<1|1]+=delta[now]*(r-mid);
maxn[now<<1]+=delta[now];maxn[now<<1|1]+=delta[now];
delta[now<<1]+=delta[now];delta[now<<1|1]+=delta[now];
delta[now]=0;
}
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
sum[now]=maxn[now]=h[pt[l]]-1;
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void change(int now,int l,int r,int lr,int rr,int x)
{
int mid=(l+r)>>1;
if (lr<=l&&r<=rr)
{
sum[now]+=x*(r-l+1);
maxn[now]+=x;
delta[now]+=x;
return;
}
pushdown(now,l,r,mid);
if (lr<=mid) change(now<<1,l,mid,lr,rr,x);
if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,x);
update(now);
}
int query_sum(int now,int l,int r,int lr,int rr)
{
int mid=(l+r)>>1,ans=0;
if (lr<=l&&r<=rr) return sum[now];
pushdown(now,l,r,mid);
if (lr<=mid) ans+=query_sum(now<<1,l,mid,lr,rr);
if (mid+1<=rr) ans+=query_sum(now<<1|1,mid+1,r,lr,rr);
return ans;
}
int query_max(int now,int l,int r,int lr,int rr)
{
int mid=(l+r)>>1,ans=0;
if (lr<=l&&r<=rr) return maxn[now];
pushdown(now,l,r,mid);
if (lr<=mid) ans=max(ans,query_max(now<<1,l,mid,lr,rr));
if (mid+1<=rr) ans=max(ans,query_max(now<<1|1,mid+1,r,lr,rr));
return ans;
}
//------------------------------lct------------------------------------
bool isroot(int x)
{
return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
int get(int x)
{
return ch[fa[x]][1]==x;
}
void rotate(int x)
{
int old=fa[x],oldf=fa[old],wh=get(x);
if (!isroot(old)) ch[oldf][ch[oldf][1]==old]=x;
fa[x]=oldf;
ch[old][wh]=ch[x][wh^1];
if (ch[old][wh]) fa[ch[old][wh]]=old;
ch[x][wh^1]=old;
fa[old]=x;
}
void splay(int x)
{
for (int old;!isroot(x);rotate(x))
if (!isroot(old=fa[x]))
rotate((get(x)==get(old))?old:x);
}
int find(int x)
{
while (ch[x][0]) x=ch[x][0];
return x;
}
void access(int x)
{
int t=0;
for (;x;t=x,x=fa[x])
{
splay(x);
if (ch[x][1])
{
int rt=find(ch[x][1]);
change(1,1,n,in[rt],out[rt],1);
}
ch[x][1]=t;
if (t)
{
int rt=find(t);
change(1,1,n,in[rt],out[rt],-1);
}
}
}
//------------------------------main-----------------------------------
int main()
{
freopen("paint.in","r",stdin);
freopen("paint.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;iint x,y;scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
build(1,1,n);
for (int i=1;i<=n;++i) fa[i]=father[i];
while (m--)
{
int opt;scanf("%d",&opt);
if (opt==1)
{
int x;scanf("%d",&x);
access(x);
}
else if (opt==2)
{
int x,y;scanf("%d%d",&x,&y);
int r=lca(x,y),old=father[r];
int ans=query_sum(1,1,n,in[x],in[x])+query_sum(1,1,n,in[y],in[y]);
ans-=query_sum(1,1,n,in[r],in[r])*2;
printf("%d\n",ans+1);
}
else if (opt==3)
{
int x;scanf("%d",&x);
int ans=query_max(1,1,n,in[x],out[x]);
printf("%d\n",ans+1);
}
}
}
求有多少个序列,满足:共有n个数,每一个数都是不超过m的整数,这n个数的和为p的倍数,并且这n个数中至少有一个为质数。
对20140408取模。
对于20%的数据, 1≤n≤100,1≤m≤100
对于50%的数据, 1≤m≤100
对于80%的数据, 1≤m≤106
对于100%的数据, 1≤n≤109,1≤m≤2⋅107,1≤p≤100
首先容斥一下
令f(i,j)表示前i个数,和为j,没有一个数是质数的方案数
令g(i,j)表示前i个数,和为j的方案数
然后枚举p的每一个倍数k,计算 ∑g(n,k)−f(n,k)
令f(i,j)表示前i个数,和对p取模之后为j,没有一个数是质数的方案数
令g(i,j)表示前i个数,和对p取模之后为j的方案数
那么答案应该是g(n,0)-f(n,0)
只需要线筛预处理出来 2⋅107 范围内的数有多少个对p取模为i,不是质数的数有多少个对p取模为i就可以实现 O(np) 枚举 O(p) 转移了
由于n比较大,可以用矩乘优化
构造矩阵时,如果f(i,j)能转移到f(i+1,k),系数为w,那么就在转移矩阵的第i行第j列置w,这样构造矩阵的复杂度是 O(p2) 的
总时间 O(lognp3)
这道题在考场上拿到了满分,写的时间也不算长,还是比较好的。
#include
#include
#include
#include
#include
using namespace std;
#define Mod 20170408
#define LL long long
int n,m,p;
int prime[2000005];
bool isp[20000005];
LL cnt[105][2],ans;
struct data{LL a[105][105];}unit,f,g,F,G;
void get(int n)
{
isp[1]=1;
for (int i=2;i<=n;++i)
{
if (!isp[i]) prime[++prime[0]]=i;
for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
{
isp[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
for (int i=1;i<=n;++i)
{
if (isp[i])
{
++cnt[i%p][0];
if (cnt[i%p][0]==Mod) cnt[i%p][0]=0;
}
++cnt[i%p][1];
if (cnt[i%p][1]==Mod) cnt[i%p][1]=0;
}
}
data cheng(data a,data b)
{
data ans;memset(ans.a,0,sizeof(ans.a));
for (int k=0;kfor (int i=0;i
for (int j=0;j
*b.a[k][j]%Mod)%Mod;
return ans;
}
data fast_pow(data a,int p)
{
data ans=unit;
for (;p;p>>=1,a=cheng(a,a))
if (p&1)
ans=cheng(ans,a);
return ans;
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%d%d%d",&n,&m,&p);
get(m);
for (int i=0;i
1;
for (int i=0;i
0][i]=cnt[i][0];
g.a[0][i]=cnt[i][1];
}
for (int i=0;i
for (int j=0;j
%p]=cnt[j][0];
G.a[i][(i+j)%p]=cnt[j][1];
}
F=fast_pow(F,n-1);
G=fast_pow(G,n-1);
f=cheng(f,F);
g=cheng(g,G);
ans=g.a[0][0]-f.a[0][0];
ans=(ans+Mod)%Mod;
printf("%I64d\n",ans);
}
有n个男生和n个女生,其中第i个男生和第j个女生配对的数据分别为 aij 和 bij
求一种配对方案,使 ∑aij∑bij 最大
对10%的数据, 1≤n≤5
对40%的数据, 1≤n≤18
对另外20%的数据, bij=1
对100%的数据, 1≤n≤100,1≤ai,j,bi,j≤104
O(n!∗n) 暴力枚举+判断
据说是给写正解结果tle的人的?
因为分母一定,直接是最大权值匹配,费用流即可
这个式子一看就是01分数规划嘛
首先二分答案,设 ∑aij∑bij=mid ,那么当 ∑aij∑bij≥mid 即 ∑aij−∑bij∗mid≥0 的时候说明还存在更优解
于是每一次将两个人的配对的权值改成 aij−bij∗mid 然后用费用流做最大权值匹配,若匹配出来大于或等于0说明还有更优解
faebdc的做法是将所有的数扩大 107 这样来避免精度误差,感觉很厉害啊
向ATP学习了KM算法了之后又写了一遍,真的跑的好快啊
这道题在考场上拿到了90分,10分是因为精度问题。因为当时怕tle所以将精度开得较大。这种小问题如果再对拍久一些的话是有可能发现的,以后注意吧
费用流
#include
#include
#include
#include
#include
#include
using namespace std;
const double eps=1e-8;
int n,s,t,maxflow;
int tot,point[210],nxt[31000],v[31000],remain[31000],last[210];double c[31000];
double ans,maxcost;
double a[105][105],b[105][105],dis[210];bool vis[210];
queue <int> q;
void addedge(int x,int y,int cap,double z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; c[tot]=-z;
}
int addflow(int s,int t)
{
int now=t,ans=1000000000;
while (now!=s)
{
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}
now=t;
while (now!=s)
{
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
bool spfa(int s,int t)
{
for (int i=1;i<=t;++i) dis[i]=-1e9;dis[s]=0;
memset(vis,0,sizeof(vis));vis[s]=1;
q.push(s);
while (!q.empty())
{
int now=q.front();q.pop();
vis[now]=0;
for (int i=point[now];i!=-1;i=nxt[i])
if (dis[v[i]]-dis[now]-c[i]<-eps&&remain[i])
{
dis[v[i]]=dis[now]+c[i];
last[v[i]]=i;
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
if (dis[t]==-1e9) return 0;
int flow=addflow(s,t);
maxflow+=flow;
maxcost+=(double)flow*dis[t];
if (maxcost<-eps) return 0;
return 1;
}
bool check(double mid)
{
tot=-1;memset(point,-1,sizeof(point));
s=n+n+1,t=s+1;
for (int i=1;i<=n;++i) addedge(s,i,1,0),addedge(n+i,t,1,0);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
addedge(i,n+j,1,a[i][j]-mid*b[i][j]);
maxflow=0;maxcost=0.0;
while (spfa(s,t));
return maxcost>=-eps;
}
double find()
{
double l=1,r=1e4,mid,ans=0;
while (r-l>eps)
{
mid=(l+r)/2.0;
if (check(mid)) ans=l=mid;
else r=mid;
}
return ans;
}
int main()
{
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%lf",&a[i][j]);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%lf",&b[i][j]);
ans=find();
printf("%.6lf\n",ans);
}
KM
#include
#include
#include
#include
#include
using namespace std;
const double eps=1e-8;
const double inf=1e9;
int n,mak;
int link[105],vis[210];
double ans;
double a[105][105],b[105][105],ex[105],ey[105],e[105][105],lak[105];
bool find(int x,int mak)
{
vis[x]=mak;
for (int i=1;i<=n;++i)
if (vis[i+n]!=mak)
{
double tmp=ex[x]+ey[i]-e[x][i];
if (fabs(tmp)if (link[i]==-1||find(link[i],mak))
{
link[i]=x;
return 1;
}
}
else lak[i]=min(lak[i],tmp);
}
return 0;
}
double KM()
{
double sum=0;
for (int i=1;i<=n;++i)
{
ex[i]=-inf;
for (int j=1;j<=n;++j)
ex[i]=max(ex[i],e[i][j]);
}
memset(ey,0,sizeof(ey));
for (int i=1;i<=n;++i)
{
for (int j=1;j<=n;++j) lak[j]=inf;
while (1)
{
++mak;
if (find(i,mak)) break;
double tmp=inf;
for (int k=1;k<=n;++k)
if (vis[k+n]!=mak) tmp=min(tmp,lak[k]);
for (int k=1;k<=n;++k)
{
if (vis[k]==mak) ex[k]-=tmp;
if (vis[k+n]==mak) ey[k]+=tmp;
else lak[k]-=tmp;
}
}
}
for (int i=1;i<=n;++i)
if (link[i]!=-1)
sum+=e[link[i]][i];
return sum;
}
bool check(double mid)
{
memset(vis,0,sizeof(vis));
mak=0;memset(link,-1,sizeof(link));
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
e[i][j]=(double)a[i][j]-mid*b[i][j];
double ans=KM();
return ans>-eps;
}
double find()
{
double l=1,r=1e4,mid,ans=0;
while (r-l>eps)
{
mid=(l+r)/2.0;
if (check(mid)) ans=l=mid;
else r=mid;
}
return ans;
}
int main()
{
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%lf",&a[i][j]);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%lf",&b[i][j]);
ans=find();
printf("%.6lf\n",ans);
}
有n个人,每一个人有一个由H和T组成,长度为m的序列,并且这n个序列两两不同。
现在要一位一位构造一个新的序列,其中每一个位置是H和T的概率相等,并且如果n个序列中某一个序列i在这个新序列中出现了,那么立刻停止,并且判第i个人获胜。
求每一个人获胜的概率。
相对误差 ≤10−6 即为正确。
对10%的数据, 1≤n,m≤3
对40%的数据, 1≤n,m≤18
对另外20%的数据, n=2
对100%的数据, 1≤n,m≤300
考虑状压dp,f(i,s)表示选了i个,最后m个状态为s的概率
因为做了很多次了之后概率就变得非常小了,所以当i比较大的时候就非常接近答案了
同样是dp,先对n个串建立AC自动机,然后f(i,j)表示选了i个,匹配到自动机的第j个节点的概率
同样是做很多次,但是这样的话自动机的状态非常少,比之前的状压的方法要优越一些
这道题的关键思路是用一个变量 P(N) 直接表示所有没有到达终止点的概率
若两个串是TTH和HTT,假设 P(A) 和 P(B) 分别表示到达这两个人的终止点的概率
那么如果N接上TTH,那么一定到达了一个终止点,但是有可能到不了最后一个T就结束了,因为可能到达了B的结束状态,所以考虑所有的可能性可以得到
这道题在考试的时候只拿到了10分,想到了AC自动机,但是没有想到在AC自动机上dp,更没有想到正解。
感觉这道题的思路还是很巧妙的。
kmp
#include
#include
#include
#include
#include
using namespace std;
#define N 605
int n,m,len;
char s[N][N],w[N];
int T[N];
double mi[N],a[N][N],b[N],ans[N];
void calc_T()
{
memset(T,0,sizeof(T));
T[0]=-1;
for (int i=0;iint j=T[i];
while (j!=-1&&w[i]!=w[j])
j=T[j];
T[i+1]=++j;
}
}
void gauss()
{
for (int i=1;i<=n+1;++i)
{
int num=i;
for (int j=i+1;j<=n+1;++j)
if (fabs(a[j][i])>fabs(a[i][i])) num=j;
if (num!=i)
{
for (int j=1;j<=n+1;++j) swap(a[i][j],a[num][j]);
swap(b[i],b[num]);
}
for (int j=i+1;j<=n+1;++j)
if (fabs(a[j][i]))
{
double t=a[j][i]/a[i][i];
for (int k=1;k<=n+1;++k)
a[j][k]-=t*a[i][k];
}
}
for (int i=n+1;i>=1;--i)
{
for (int j=i+1;j<=n+1;++j) b[i]-=a[i][j]*ans[j];
ans[i]=b[i]/a[i][i];
}
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) scanf("%s",s[i]+1);
mi[0]=1.0;for (int i=1;i<=m;++i) mi[i]=mi[i-1]*0.5;
for (int i=1;i<=n;++i)
{
a[i][n+1]=-mi[m];
for (int j=1;j<=n;++j)
{
len=0;
for (int k=1;k<=m;++k) w[len++]=s[i][k];
w[len++]='$';
for (int k=1;k<=m;++k) w[len++]=s[j][k];
calc_T();
int now=T[len];
while (now>0)
{
a[i][j]+=mi[m-now];
now=T[now];
}
}
}
for (int i=1;i<=n;++i) a[n+1][i]=1.0;
b[n+1]=1.0;
gauss();
for (int i=1;i<=n;++i) printf("%.10lf\n",ans[i]);
}
AC自动机
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 305
int n,m,sz;
char s[N][N];
int ch[N*N][2],fail[N*N],h[N*N],is_end[N*N],pt[N];
int tot,point[N*N],nxt[N*N],v[N*N];
double mi[N],a[N][N],b[N],ans[N];
queue <int> q;
void add(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void insert(int id)
{
int now=0;
for (int i=1;i<=m;++i)
{
int x=(s[id][i]=='T')?1:0;
if (!ch[now][x]) ch[now][x]=++sz;
h[ch[now][x]]=h[now]+1;
now=ch[now][x];
is_end[now]=1;
add(now,id);
}
pt[id]=now;
}
void make_fail()
{
for (int i=0;i<=1;++i)
if (ch[0][i]) q.push(ch[0][i]);
while (!q.empty())
{
int now=q.front();q.pop();
for (int i=0;i<=1;++i)
{
if (!ch[now][i])
{
ch[now][i]=ch[fail[now]][i];
continue;
}
fail[ch[now][i]]=ch[fail[now]][i];
q.push(ch[now][i]);
}
}
}
void gauss()
{
for (int i=1;i<=n+1;++i)
{
int num=i;
for (int j=i+1;j<=n+1;++j)
if (fabs(a[j][i])>fabs(a[i][i])) num=j;
if (num!=i)
{
for (int j=1;j<=n+1;++j) swap(a[i][j],a[num][j]);
swap(b[i],b[num]);
}
for (int j=i+1;j<=n+1;++j)
if (fabs(a[j][i]))
{
double t=a[j][i]/a[i][i];
for (int k=1;k<=n+1;++k)
a[j][k]-=t*a[i][k];
}
}
for (int i=n+1;i>=1;--i)
{
for (int j=i+1;j<=n+1;++j) b[i]-=a[i][j]*ans[j];
ans[i]=b[i]/a[i][i];
}
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d%d",&n,&m);
mi[0]=1.0;for (int i=1;i<=m;++i) mi[i]=mi[i-1]*0.5;
for (int i=1;i<=n;++i)
{
scanf("%s",s[i]+1);
insert(i);
}
make_fail();
for (int i=1;i<=n;++i)
{
a[i][n+1]=-mi[m];
int now=pt[i];
while (now)
{
if (is_end[now])
{
for (int j=point[now];j;j=nxt[j])
a[v[j]][i]+=mi[m-h[now]];
}
now=fail[now];
}
}
for (int i=1;i<=n;++i) a[n+1][i]=1.0;
b[n+1]=1.0;
gauss();
for (int i=1;i<=n;++i) printf("%.10lf\n",ans[i]);
}
有两个n个数的数列 xi 和 yi ,有m个操作,3种操作类型
1 L R
令 x¯=1R−L+1∑i=LRxi,y¯=1R−L+1∑i=LRyi ,计算 a=∑i=LR(xi−x¯)(yi−y¯)∑i=LR(xi−x¯)2
2 L R S T
对于每一个 L≤i≤R , xi 加上S, yi 加上T
3 L R S T
对于每一个 L≤i≤R , xi 修改为(S+i), yi 修改为(T+i)
对20%的数据, 1≤n,m≤1000
对另外20%的数据,没有3操作,且2操作中S=0
对另外30%的数据,没有3操作
对于100%的数据, 1≤n,m≤105,1≤L≤R≤n,0≤|S|,|T|≤105,0≤|xi|,|yi|≤105
暴力暴力
首先将要求的a的式子划开来,变成
那么40pts和70pts就是给正解想不全的人吧。。。
这道题在考场上拿到了满分,但其实不是刚开始思路就很清晰,是边写边发现可做的。因为加法标记和覆盖标记写得比较熟所以总体来说还是不错的。
#include
#include
#include
#include
#include
using namespace std;
#define N 100005
const double inf=1e18;
int n,m;
double xiyi,xi,yi,xx;
double x[N],y[N],sum[N],sum2[N];
double sxy[N*4],sxx[N*4],sx[N*4],sy[N*4],ds[N*4],dt[N*4],cs[N*4],ct[N*4];
void update(int now)
{
sxy[now]=sxy[now<<1]+sxy[now<<1|1];
sx[now]=sx[now<<1]+sx[now<<1|1];
sy[now]=sy[now<<1]+sy[now<<1|1];
sxx[now]=sxx[now<<1]+sxx[now<<1|1];
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
cs[now]=ct[now]=inf;
if (l==r)
{
sxy[now]=x[l]*y[l];
sx[now]=x[l];
sy[now]=y[l];
sxx[now]=x[l]*x[l];
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void pushdown(int now,int l,int r,int mid)
{
if (ds[now]||dt[now])
{
sxy[now<<1]+=dt[now]*sx[now<<1]+ds[now]*sy[now<<1]+ds[now]*dt[now]*(mid-l+1+0.0);
sxx[now<<1]+=2*ds[now]*sx[now<<1]+ds[now]*ds[now]*(mid-l+1+0.0);
sx[now<<1]+=ds[now]*(mid-l+1+0.0);
sy[now<<1]+=dt[now]*(mid-l+1+0.0);
if (cs[now<<1]!=inf||ct[now<<1]!=inf) cs[now<<1]+=ds[now],ct[now<<1]+=dt[now];
else ds[now<<1]+=ds[now],dt[now<<1]+=dt[now];
sxy[now<<1|1]+=dt[now]*sx[now<<1|1]+ds[now]*sy[now<<1|1]+ds[now]*dt[now]*(r-mid+0.0);
sxx[now<<1|1]+=2*ds[now]*sx[now<<1|1]+ds[now]*ds[now]*(r-mid+0.0);
sx[now<<1|1]+=ds[now]*(r-mid+0.0);
sy[now<<1|1]+=dt[now]*(r-mid+0.0);
if (cs[now<<1|1]!=inf||ct[now<<1|1]!=inf) cs[now<<1|1]+=ds[now],ct[now<<1|1]+=dt[now];
else ds[now<<1|1]+=ds[now],dt[now<<1|1]+=dt[now];
ds[now]=0;dt[now]=0;
}
if (cs[now]!=inf||ct[now]!=inf)
{
sxy[now<<1]=cs[now]*ct[now]*(mid-l+1+0.0)+(sum[mid]-sum[l-1])*(cs[now]+ct[now])+sum2[mid]-sum2[l-1];
sxx[now<<1]=cs[now]*cs[now]*(mid-l+1+0.0)+2*cs[now]*(sum[mid]-sum[l-1])+sum2[mid]-sum2[l-1];
sx[now<<1]=cs[now]*(mid-l+1+0.0)+sum[mid]-sum[l-1];
sy[now<<1]=ct[now]*(mid-l+1+0.0)+sum[mid]-sum[l-1];
ds[now<<1]=dt[now<<1]=0;
cs[now<<1]=cs[now];ct[now<<1]=ct[now];
sxy[now<<1|1]=cs[now]*ct[now]*(r-mid+0.0)+(sum[r]-sum[mid])*(cs[now]+ct[now])+sum2[r]-sum2[mid];
sxx[now<<1|1]=cs[now]*cs[now]*(r-mid+0.0)+2*cs[now]*(sum[r]-sum[mid])+sum2[r]-sum2[mid];
sx[now<<1|1]=cs[now]*(r-mid+0.0)+sum[r]-sum[mid];
sy[now<<1|1]=ct[now]*(r-mid+0.0)+sum[r]-sum[mid];
ds[now<<1|1]=dt[now<<1|1]=0;
cs[now<<1|1]=cs[now];ct[now<<1|1]=ct[now];
cs[now]=inf;ct[now]=inf;
}
}
void change(int now,int l,int r,int lr,int rr,double s,double t,int opt)
{
int mid=(l+r)>>1;
if (l!=r) pushdown(now,l,r,mid);
if (lr<=l&&r<=rr)
{
if (!opt)
{
sxy[now]+=t*sx[now]+s*sy[now]+s*t*(r-l+1+0.0);
sxx[now]+=2*s*sx[now]+s*s*(r-l+1+0.0);
sx[now]+=s*(r-l+1+0.0);
sy[now]+=t*(r-l+1+0.0);
if (cs[now]!=inf||ct[now]!=inf) cs[now]+=s,ct[now]+=t;
else ds[now]+=s,dt[now]+=t;
}
else
{
sxy[now]=s*t*(r-l+1+0.0)+(sum[r]-sum[l-1])*(s+t)+sum2[r]-sum2[l-1];
sxx[now]=s*s*(r-l+1+0.0)+2*s*(sum[r]-sum[l-1])+sum2[r]-sum2[l-1];
sx[now]=s*(r-l+1+0.0)+sum[r]-sum[l-1];
sy[now]=t*(r-l+1+0.0)+sum[r]-sum[l-1];
ds[now]=dt[now]=0;
cs[now]=s;ct[now]=t;
}
return;
}
if (lr<=mid) change(now<<1,l,mid,lr,rr,s,t,opt);
if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,s,t,opt);
update(now);
}
void query(int now,int l,int r,int lr,int rr)
{
int mid=(l+r)>>1;
if (l!=r) pushdown(now,l,r,mid);
if (lr<=l&&r<=rr)
{
xiyi+=sxy[now];
xi+=sx[now];
yi+=sy[now];
xx+=sxx[now];
return;
}
if (lr<=mid) query(now<<1,l,mid,lr,rr);
if (mid+1<=rr) query(now<<1|1,mid+1,r,lr,rr);
}
int main()
{
freopen("relative.in","r",stdin);
freopen("relative.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) scanf("%lf",&x[i]);
for (int i=1;i<=n;++i) scanf("%lf",&y[i]);
for (int i=1;i<=n;++i) sum[i]=sum[i-1]+(double)i,sum2[i]=sum2[i-1]+(double)i*(double)i;
build(1,1,n);
while (m--)
{
int opt;scanf("%d",&opt);
if (opt==1)
{
int l,r;scanf("%d%d",&l,&r);
xiyi=xi=yi=xx=0;
query(1,1,n,l,r);
double xba=xi/(r-l+1+0.0);
double yba=yi/(r-l+1+0.0);
double up=xiyi-xba*yi-yba*xi+xba*yba*(r-l+1+0.0);
double down=xx-2*xba*xi+xba*xba*(r-l+1+0.0);
double ans=up/down;
printf("%.10lf\n",ans);
}
else if (opt==2)
{
int l,r;scanf("%d%d",&l,&r);
double s,t;scanf("%lf%lf",&s,&t);
change(1,1,n,l,r,s,t,0);
}
else if (opt==3)
{
int l,r;scanf("%d%d",&l,&r);
double s,t;scanf("%lf%lf",&s,&t);
change(1,1,n,l,r,s,t,1);
}
}
}