A. 要换换名字
分析
暴力的做法就是二分一个长度 (len)
每一个字符串向所有长度小于等于 (len) 的子序列连边
然后用 (dinic) 跑一个二分图最大匹配即可
如果能匹配到n个就返回 (1)
否则返回 (0)
发现如果一个字符串有多于 (n) 个子序列,那么一定能匹配
所以只需要 (bfs) 找出长度前 (n) 小的子序列即可
分析
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<queue>
#include<string>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=2e5+5,maxm=305;
char ss[maxm][maxm];
int n,len[maxm],nxt[maxm][maxm][28],cnt,l,r,mids;
std::map<std::string,int> mp;
std::string rk[maxn];
struct jie{
int len,id;
std::string val;
jie(){}
jie(rg int aa,rg int bb,std::string cc){
len=aa,id=bb,val=cc;
}
};
std::vector<jie> g[maxm];
void init(rg int id){
std::queue<jie> q;
std::string tmp;
for(rg int i=0;i<26;i++){
rg int now=nxt[id][0][i];
tmp='a'+i;
if(now){
q.push(jie(1,now,tmp));
}
}
rg int ncnt=0;
while(!q.empty()){
rg int tmp1=q.front().id,tmp2=q.front().len;
std::string tmp3=q.front().val;
q.pop();
if(!mp[tmp3]){
mp[tmp3]=++cnt;
rk[cnt]=tmp3;
}
g[id].push_back(jie(tmp2,mp[tmp3],tmp3));
ncnt++;
if(ncnt>=n) return;
for(rg int i=0;i<26;i++){
if(nxt[id][tmp1][i]){
tmp=tmp3+ss[id][nxt[id][tmp1][i]];
q.push(jie(tmp2+1,nxt[id][tmp1][i],tmp));
}
}
}
}
int h[maxn],tot=2,mmax,h2[maxn],dep[maxn],q[maxn],head,tail,s,t;
struct asd{
int to,nxt,val;
}b[maxn];
void ad(int aa,int bb,int cc){
b[tot].to=bb;
b[tot].nxt=h[aa];
b[tot].val=cc;
h[aa]=tot++;
}
bool bfs(){
for(rg int i=0;i<=mmax;i++){
dep[i]=0;
h[i]=h2[i];
}
q[head=tail=1]=s;
dep[s]=1;
while(head<=tail){
rg int now=q[head++];
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(!dep[u] && b[i].val){
dep[u]=dep[now]+1;
q[++tail]=u;
}
}
}
return dep[t];
}
int dfs(int now,int ac1){
if(now==t) return ac1;
int ac2=0;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
h[now]=i;
rg int u=b[i].to;
if(dep[u]==dep[now]+1 && b[i].val){
rg int nans=dfs(u,std::min(ac1,b[i].val));
ac1-=nans;
ac2+=nans;
b[i].val-=nans;
b[i^1].val+=nans;
}
if(ac1==0) break;
}
if(ac2==0) dep[now]=0;
return ac2;
}
int vis[maxn],tim;
bool jud(rg int val){
memset(h,-1,sizeof(h));
tot=2,s=cnt+n+1,t=cnt+n+2;
for(rg int i=1;i<=n;i++){
ad(s,i+cnt,1),ad(i+cnt,s,0);
}
tim++;
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<g[i].size();j++){
if(g[i][j].len<=val){
rg int now=g[i][j].id;
if(vis[now]!=tim) ad(now,t,1),ad(t,now,0);
ad(i+cnt,now,1),ad(now,i+cnt,0);
vis[now]=tim;
}
}
}
mmax=t;
for(rg int i=1;i<=mmax;i++) h2[i]=h[i];
rg int ans=0;
while(bfs()) ans+=dfs(s,1e9);
return ans==n;
}
int main(){
n=read();
for(rg int i=1;i<=n;i++) scanf("%s",ss[i]+1);
for(rg int i=1;i<=n;i++) len[i]=strlen(ss[i]+1);
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<=len[i];j++){
for(rg int k=j+1;k<=len[i];k++){
rg int tmp=ss[i][k]-'a';
if(!nxt[i][j][tmp]){
nxt[i][j][tmp]=k;
}
}
}
}
for(rg int i=1;i<=n;i++) init(i);
for(rg int i=1;i<=n;i++) r=std::max(r,len[i]);
l=1;
while(l<=r){
mids=(l+r)>>1;
if(jud(mids)) r=mids-1;
else l=mids+1;
}
if(!jud(l)) printf("-1
");
else {
printf("%d
",l);
for(rg int i=1;i<=n;i++){
for(rg int j=h[i+cnt];j!=-1;j=b[j].nxt){
if(b[j].val==0){
std::cout<<rk[b[j].to]<<std::endl;
break;
}
}
}
}
return 0;
}
B. 动态半平面交
分析
貌似强制在线的做法空间复杂度是 (log^2n)的,所以这道题没有强制在线
首先把一个质数 (p) 拆成 (p,p^2,p^3 cdots p^k),每一个对答案的贡献都是 (p)
考虑如何去掉重复的贡献
在序列上的做法是记录一个(pre[x]) 表示 (x) 上一次出现的位置
当把新的 (x) 加入的时候把 (pre[x]) 的贡献减掉即可
把这个问题搬到树上,就是把所有的节点按照 (dfn) 序从小到大排序,把相邻两个点之间 (lca) 的贡献去掉
之所以可以这么做是因为我们查询的是整个子树的贡献,它的 (dfn) 序是连续的
因为是动态的所以要拿 (set) 维护
然后把所有询问按照深度从小到大排序,同时把所有的点按照深度从小到大排序依次加入
维护一棵以 (dfn) 序为下标的线段树
那么要进行的操作就是单点修改和区间查询
时间复杂度是 (O(nlog^2n)) 的
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<vector>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e5+5,mod=998244353;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
inline int ksm(rg int ds,rg int zs){
rg int nans=1;
while(zs){
if(zs&1) nans=mulmod(nans,ds);
ds=mulmod(ds,ds);
zs>>=1;
}
return nans;
}
int h[maxn],tot=1,n,q;
struct asd{
int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
int fa[maxn],siz[maxn],dep[maxn],son[maxn];
void dfs1(rg int now,rg int lat){
fa[now]=lat;
dep[now]=dep[lat]+1;
siz[now]=1;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
dfs1(u,now);
siz[now]+=siz[u];
if(son[now]==0 || siz[u]>siz[son[now]]) son[now]=u;
}
}
int tp[maxn],rk[maxn],dfn[maxn],dfnc;
void dfs2(rg int now,rg int top){
dfn[now]=++dfnc;
tp[now]=top;
rk[dfnc]=now;
if(son[now]) dfs2(son[now],top);
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==son[now] || u==fa[now]) continue;
dfs2(u,u);
}
}
int getlca(rg int xx,rg int yy){
while(tp[xx]!=tp[yy]){
if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
xx=fa[tp[xx]];
}
return dep[xx]<dep[yy]?xx:yy;
}
struct tree{
int l,r,val;
}tr[maxn<<2];
void push_up(rg int da){
tr[da].val=mulmod(tr[da<<1].val,tr[da<<1|1].val);
}
void build(rg int da,rg int l,rg int r){
tr[da].l=l,tr[da].r=r,tr[da].val=1;
if(l==r) return;
rg int mids=(l+r)>>1;
build(da<<1,l,mids),build(da<<1|1,mids+1,r);
}
void xg(rg int da,rg int wz,rg int val){
if(tr[da].l==tr[da].r){
tr[da].val=mulmod(tr[da].val,val);
return;
}
rg int mids=(tr[da].l+tr[da].r)>>1;
if(wz<=mids) xg(da<<1,wz,val);
else xg(da<<1|1,wz,val);
push_up(da);
}
int cx(rg int da,rg int l,rg int r){
if(tr[da].l>=l && tr[da].r<=r) return tr[da].val;
rg int mids=(tr[da].l+tr[da].r)>>1,nans=1;
if(l<=mids) nans=mulmod(nans,cx(da<<1,l,r));
if(r>mids) nans=mulmod(nans,cx(da<<1|1,l,r));
return nans;
}
int a[maxn],sta[maxn*20],stacnt,id[maxn];
struct longdie{
int val,num;
longdie();
longdie(rg int aa,rg int bb){
val=aa,num=bb;
}
};
std::vector<longdie> g[maxn];
void divid(rg int id,rg int val){
for(rg int i=2;i*i<=val;i++){
if(val%i==0){
rg int now=1;
while(val%i==0){
now*=i,val/=i;
g[id].push_back(longdie(i,now));
sta[++stacnt]=now;
}
}
}
if(val>1){
g[id].push_back(longdie(val,val));
sta[++stacnt]=val;
}
}
int ans[maxn];
struct jie{
int maxdep,num,id;
jie(){}
jie(rg int aa,rg int bb,rg int cc){
maxdep=aa,num=bb,id=cc;
}
}c[maxn];
bool cmp(rg jie aa,rg jie bb){
return aa.maxdep<bb.maxdep;
}
bool cmp2(rg int aa,rg int bb){
return dep[aa]<dep[bb];
}
std::set<int> s[maxn*20];
#define sit std::set<int>::iterator
void updat(rg int num){
rg int aa,bb,cc;
rg sit it1,it2,it3;
for(rg int i=0;i<g[num].size();i++){
aa=g[num][i].val,bb=g[num][i].num;
xg(1,dfn[num],aa);
s[bb].insert(dfn[num]);
if(s[bb].size()!=1){
it1=it2=it3=s[bb].lower_bound(dfn[num]);
if(it1==s[bb].begin()){
++it2;
cc=getlca(num,rk[*it2]);
xg(1,dfn[cc],ksm(aa,mod-2));
} else if(++it3==s[bb].end()){
--it2;
cc=getlca(num,rk[*it2]);
xg(1,dfn[cc],ksm(aa,mod-2));
} else {
++it2,--it1;
cc=getlca(num,rk[*it1]);
xg(1,dfn[cc],ksm(aa,mod-2));
cc=getlca(num,rk[*it2]);
xg(1,dfn[cc],ksm(aa,mod-2));
cc=getlca(rk[*it1],rk[*it2]);
xg(1,dfn[cc],aa);
}
}
}
}
int main(){
memset(h,-1,sizeof(h));
n=read(),n=read();
for(rg int i=1;i<=n;i++) a[i]=read();
for(rg int i=1;i<=n;i++) divid(i,a[i]);
std::sort(sta+1,sta+1+stacnt);
stacnt=std::unique(sta+1,sta+1+stacnt)-sta-1;
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<g[i].size();j++){
g[i][j].num=std::lower_bound(sta+1,sta+1+stacnt,g[i][j].num)-sta;
}
}
rg int aa,bb;
for(rg int i=1;i<n;i++){
aa=read(),bb=read();
ad(aa,bb),ad(bb,aa);
}
dfs1(1,0),dfs2(1,1);
build(1,1,n);
for(rg int i=1;i<=n;i++) id[i]=i;
std::sort(id+1,id+n+1,cmp2);
q=read();
for(rg int i=1;i<=q;i++){
aa=read(),bb=read();
c[i]=jie(std::min(dep[aa]+bb,n),aa,i);
}
std::sort(c+1,c+q+1,cmp);
rg int head=1,now=1;
for(rg int nowdep=1;nowdep<=n;nowdep++){
while(now<=n && dep[id[now]]==nowdep) updat(id[now++]);
while(head<=q && c[head].maxdep==nowdep){
ans[c[head].id]=cx(1,dfn[c[head].num],dfn[c[head].num]+siz[c[head].num]-1);
head++;
}
}
for(rg int i=1;i<=q;i++) printf("%d
",ans[i]);
return 0;
}
C. 获取名额
分析
要求的式子就是 (1-prodlimits_{i=l}^r(1-frac{a_i}{x}))
发现 (prodlimits_{i=l}^r(1-frac{a_i}{x})=exp(sumlimits_{i=l}^rln(1-frac{a_i}{x})))
对于 (ln(1-x)) 进行泰勒展开可以得到 (-sumlimits_{i=1}^{+infty}frac{x^j}{j})
但是发现当 (1-x) 过小时(ln) 的绝对值过大,容易丢精
所以设一个阈值,(1-x) 小于这个值的时候暴力算,否则用泰勒展开的前 (30) 项
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=6e5+5;
int n,q,l,r,wz[maxn][25],lg[maxn];
double a[maxn],val[maxn][25],mmax,ans,x,pre[maxn][32];
void solve(rg int l,rg int r){
if(l>r) return;
if(ans<1e-12) return;
rg int k=lg[r-l+1];
rg int mids=wz[l][k];
if(val[l][k]<val[r-(1<<k)+1][k]) mids=wz[r-(1<<k)+1][k];
rg double val=a[mids]/x;
if(val>0.4){
ans*=(1-val);
solve(l,mids-1),solve(mids+1,r);
} else {
rg double sum=0,tmp=x;
for(rg int i=1;i<=30;i++){
sum+=(pre[r][i]-pre[l-1][i])/tmp;
tmp*=x;
}
ans*=exp(-sum);
}
}
int main(){
n=read(),q=read();
for(rg int i=1;i<=n;i++) a[i]=read(),mmax=std::max(mmax,a[i]);
for(rg int i=1;i<=n;i++) a[i]/=mmax;
for(rg int i=1;i<=n;i++) wz[i][0]=i,val[i][0]=a[i];
for(rg int j=1;j<=20;j++){
for(rg int i=1;i+(1<<j)-1<=n;i++){
if(val[i][j-1]<val[i+(1<<(j-1))][j-1]) wz[i][j]=wz[i+(1<<(j-1))][j-1];
else wz[i][j]=wz[i][j-1];
val[i][j]=std::max(val[i][j-1],val[i+(1<<(j-1))][j-1]);
}
}
for(rg int i=1;i<=n;i++){
rg double tmp=a[i];
for(rg int j=1;j<=30;j++){
pre[i][j]=pre[i-1][j]+tmp/j;
tmp*=a[i];
}
}
for(rg int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(rg int i=1;i<=q;i++){
l=read(),r=read(),x=read();
ans=1,x/=mmax;
solve(l,r);
printf("%.10f
",1.0-ans);
}
return 0;
}