2018-2019 ACM-ICPC, Asia East Continent Finals部分题解

  C:显然每p2个数会有一个0循环,其中22 32 52 72的循环会在200个数中出现,找到p2循环的位置就可以知道首位在模p2意义下是多少,并且循环位置几乎是唯一的(对72不满足但可能的位置也很少)。于是这样枚举范围就直接从1e9变成了1e9/44100。然后考虑暴力求μ验证,求μ可以以O(n1/3/lnn)的时间完成,即对n1/3内的质数暴力check并除掉,剩下的直接判断是不是平方数即可。最后只要相信200个数匹配一会就break了就能过了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 210
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int prime[N],a[N],p4=-1,p9=-1,p25=-1,p49=-1,cnt=0;
inline calc(int x)
{
for (int i=1;i<=cnt;i++)
{
if (x%prime[i]==0) x/=prime[i];
if (x%prime[i]==0) return 0;
}
if (x==1) return 1;
int u=sqrt(x);
if (u*u==x||(u+1)*(u+1)==x) return 0;
else return 1;
}
signed main()
{
for (int i=2;i<=1000;i++)
{
bool flag=0;
for (int j=2;j<i;j++)
if (i%j==0) {flag=1;break;}
if (!flag) prime[++cnt]=i;
}
for (int i=0;i<200;i++) a[i]=getc()-'0';
for (int i=0;i<4;i++)
{
bool flag=0;
for (int j=i;j<200;j+=4)
if (a[j]!=0) {flag=1;break;}
if (!flag) {p4=(4-i)%4;break;}
}
if (p4==-1) {cout<<-1;return 0;}
for (int i=0;i<9;i++)
{
bool flag=0;
for (int j=i;j<200;j+=9)
if (a[j]!=0) {flag=1;break;}
if (!flag) {p9=(9-i)%9;break;}
}
if (p9==-1) {cout<<-1;return 0;}
for (int i=0;i<25;i++)
{
bool flag=0;
for (int j=i;j<200;j+=25)
if (a[j]!=0) {flag=1;break;}
if (!flag) {p25=(25-i)%25;break;}
}
if (p25==-1) {cout<<-1;return 0;}
int P=4*9*25*49,r;
for (int i=0;i<49;i++)
{
bool flag=0;
for (int j=i;j<200;j+=49)
if (a[j]!=0) {flag=1;break;}
if (!flag)
{
p49=(49-i)%49;
for (int k=0;k<P;k++)
if (k%4==p4&&k%9==p9&&k%25==p25&&k%49==p49) {r=k;break;}
for (int k=r;k+200<=1000000001;k+=P)
{
bool flag=0;
for (int x=k;x<k+200;x++)
if (calc(x)!=a[x-k]) {flag=1;break;}
if (!flag) {cout<<k;return 0;}
}
}
}
cout<<-1;
return 0;
//NOTICE LONG LONG!!!!!
}

  I:考虑将增加A的贡献直接加入dp值。要算贡献需要知道之后攻击了多少次,攻击时间的和,于是设f[i][j][k]为前i次后期望之后攻击j次攻击时间和为k的最大伤害,转移显然。倒着dp和其本质相同。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 110
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,a[N],b[N],c[N],s[N];
ll f[2][N][N*N];
signed main()
{
int T=read();
while (T--)
{
n=read();
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++) a[i]=read(),b[i]=read(),c[i]=read();
s[n]=n;
for (int i=n-1;i>=1;i--) s[i]=s[i+1]+i;
for (int i=0;i<n;i++)
{
memset(f[i&1^1],0,sizeof(f[i&1^1]));
for (int j=0;j<=n-i;j++)
for (int k=0;k<=s[i+1];k++)
{
f[i&1^1][j][k]=max(f[i&1^1][j][k],f[i&1][j][k]+1ll*(k-(i+1)*j)*b[i+1]),
f[i&1^1][j][k]=max(f[i&1^1][j][k],f[i&1][j][k]+1ll*j*c[i+1]);
if (j&&(k>=i+1))
{
f[i&1^1][j-1][k-(i+1)]=max(f[i&1^1][j-1][k-(i+1)],f[i&1][j][k]+a[i+1]);
}
}
}
cout<<f[n&1][0][0]<<endl;
}
return 0;
//NOTICE LONG LONG!!!!!
}

  J:看做一个零和博弈,那个式子看成先手的收益,相反数看成后手的收益。考虑纳什均衡,则要尽量使后手选择各后缀的收益均相同(事实上若有某后缀包含另一后缀,相同是不可能实现的,但容易发现被包含的后缀不分配概率一定最优,实际上这个分析没有任何卵用)。考虑对后缀数组以height最小值分治,这样所有跨过该位置的两后缀lcp即为该height值。递归下去算出两侧的最优答案,容易发现合并时两侧保留原本的概率分配是最优的,因为两侧自身如何分配互相之间并不相干,那么只要给两侧分配概率使后手选择两侧的收益相同即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,n,rk[N<<1],tmp[N<<1],sa[N],sa2[N],cnt[N],h[N],v[N],f[N][20],LG2[N];
char s[N];
void make()
{
int m=26;
for (int i=1;i<=n*2;i++) rk[i]=tmp[i]=0;
for (int i=1;i<=m;i++) cnt[i]=0;
for (int i=1;i<=n;i++) cnt[rk[i]=(s[i]-'a'+1)]++;
for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int p=0;
for (int i=n-k+1;i<=n;i++) sa2[++p]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
for (int i=1;i<=m;i++) cnt[i]=0;
for (int i=1;i<=n;i++) cnt[rk[i]]++;
for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=1;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
for (int i=1;i<=n*2;i++) tmp[i]=rk[i];
p=1,rk[sa[1]]=1;
for (int i=2;i<=n;i++)
{
if (tmp[sa[i]]!=tmp[sa[i-1]]||tmp[sa[i]+k]!=tmp[sa[i-1]+k]) p++;
rk[sa[i]]=p;
}
m=p;if (m==n) break;
}
for (int i=1;i<=n;i++)
{
h[i]=max(h[i-1]-1,0);
while (s[i+h[i]]==s[sa[rk[i]-1]+h[i]]) h[i]++;
}
for (int i=1;i<=n;i++) v[i]=h[sa[i]];
for (int i=1;i<=n;i++) f[i][0]=i;
for (int i=2;i<=n;i++)
{
LG2[i]=LG2[i-1];
if ((2<<LG2[i])<=i) LG2[i]++;
}
for (int j=1;j<20;j++)
for (int i=1;i<=n;i++)
if (v[f[i][j-1]]<v[f[min(n,i+(1<<j-1))][j-1]]) f[i][j]=f[i][j-1];
else f[i][j]=f[min(n,i+(1<<j-1))][j-1];
}
int query(int x,int y)
{
x++;
return v[f[x][LG2[y-x+1]]]<v[f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]]]?f[x][LG2[y-x+1]]:f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]];
}
long double solve(int l,int r)
{
if (l==r) return n-sa[l]+1;
int mid=query(l,r),h=v[mid];
long double x=solve(l,mid-1),y=solve(mid,r);
long double k=(y-h)/(x+y-h-h);
return x*k+h*(1-k);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("j.in","r",stdin);
freopen("j.out","w",stdout);
#endif
T=read();
while (T--)
{
scanf("%s",s+1);n=strlen(s+1);
make();
printf("%.12f\n",(double)solve(1,n));
}
return 0;
//NOTICE LONG LONG!!!!!
}

  K:考虑一个暴力dp,即设f[i][j]为以i为左端点要合成j时右端点至少为多少。转移显然有f[i][j]=min(f[f[i][j-1]+1][j-1],nxt[i][j]),其中nxt[i][j]为i位置之后出现的第一个j的位置。

  显然f[i][j]随i增加单调不降。这样对于询问(l,r,k),只要二分出满足f[i][k]<=r的最大i,然后答案即为(i-l+1)*(r+1)-f[l..i][k],也即要知道f数组某一段的和。

  考虑怎么不暴力地求f。观察转移,可以发现是一个类似于倍增的过程,虽然还有个取min的步骤,但仍然可以大胆猜想,对于所有j,f[i][j]构成的相同数连续段数量之和是O(nlogn)级别的。

  这样维护f数组的连续段即可。具体地,离线处理,将询问按要合成的大小从小到大排序。更新dp数组时从前往后处理,记录f的前缀和,更新完合并相邻的相同段。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,q,a[N],nxt[N],p[N],top;
ll ans[N];
struct data
{
int l,r,x,i;ll s;
bool operator <(const data&a) const
{
return x<a.x;
}
}b[N],stk[N],tmp[N];
int getdp(int x)
{
if (x>n) return n+1;
int l=1,r=top,ans;
while (l<=r)
{
int mid=l+r>>1;
if (stk[mid].l<=x) ans=mid,l=mid+1;
else r=mid-1;
}
return stk[ans].x;
}
ll getdps(int x)
{
if (x==0) return 0;
int l=1,r=top,ans=0;
while (l<=r)
{
int mid=l+r>>1;
if (stk[mid].r<=x) ans=mid,l=mid+1;
else r=mid-1;
}
return stk[ans].s+1ll*(x-stk[ans].r)*stk[ans+1].x;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("k.in","r",stdin);
freopen("k.out","w",stdout);
#endif
n=read(),m=read(),q=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=q;i++) b[i].l=read(),b[i].r=read(),b[i].x=read(),b[i].i=i;
sort(b+1,b+q+1);
for (int i=1;i<=m;i++) p[i]=n+1;
for (int i=n;i>=1;i--) nxt[i]=p[a[i]],p[a[i]]=i;
top=1;stk[1].l=1,stk[1].r=n,stk[1].x=n+1,stk[1].s=1ll*(n+1)*n;
int cur=0;
for (int i=1;i<=m;i++)
{
int x=p[i],top_tmp=0;
for (int j=1;j<=top;j++)
{
int y=getdp(stk[j].x+1),last=stk[j].l-1;
while (stk[j].r>=x)
{
top_tmp++;
tmp[top_tmp].l=last+1,tmp[top_tmp].r=x,tmp[top_tmp].x=x;
tmp[top_tmp].s=tmp[top_tmp-1].s+1ll*(x-last)*x;
last=x;x=nxt[x];
}
if (last<stk[j].r)
{
top_tmp++;
tmp[top_tmp].l=last+1,tmp[top_tmp].r=stk[j].r,tmp[top_tmp].x=min(x,y);
tmp[top_tmp].s=tmp[top_tmp-1].s+1ll*(stk[j].r-last)*min(x,y);
}
}
top=0;
for (int j=1;j<=top_tmp;j++)
{
int t=j;
while (t<top_tmp&&tmp[t+1].x==tmp[j].x) t++;
top++;stk[top].l=tmp[j].l,stk[top].r=tmp[t].r,stk[top].x=tmp[j].x,stk[top].s=tmp[t].s;
j=t;
}
for (int j=1;j<=top;j++) stk[j].s=stk[j-1].s+1ll*(stk[j].r-stk[j].l+1)*stk[j].x;
while (b[cur+1].x==i)
{
cur++;
int l=1,r=top,k=0;
while (l<=r)
{
int mid=l+r>>1;
if (stk[mid].x<=b[cur].r) k=stk[mid].r,l=mid+1;
else r=mid-1;
}
if (k>=b[cur].l) ans[b[cur].i]=1ll*(k-b[cur].l+1)*(b[cur].r+1)-(getdps(k)-getdps(b[cur].l-1));
}
//for (int j=1;j<=top;j++) cout<<stk[j].l<<' '<<stk[j].r<<' '<<stk[j].x<<' '<<stk[j].s<<endl;cout<<endl;
}
for (int i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}

  

上一篇:sessionStorage 前端HTML5会话管理


下一篇:【转】Redis学习---哈希结构内存模型剖析