XXI Open Cup named after E.V. Pankratiev, Grand Prix of Urals
链接
B. Build the String
题解
可以发现,总是能保留栈顶是 \(0\) 和 \(1\)。这样每次选择一个正确的数字 copy,然后把 copy 得到的连续段移到后方。最后两个连续段特殊处理。最后把所有连续段拼接即可。
直接做遇到 \(1010101\) 这样的串可能会超过 \(3n\) 步。考虑遇到 \(10\) 的时候直接少交换一次,稍微卡一下即可通过。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=300010;char s[N];int a[N],b[N];
vector<int>ans;
string opt[]={"copy","swap","roll","fuse"};
int main()
{
scanf("%s",s+1);int n=strlen(s+1);
for(int i=1;i<=n;i++) a[i]=s[i]-'a';
if(a[1]==0){for(int i=1;i<=n;i++) a[i]=!a[i];ans.push_back(1);}
int m=0;
for(int l=1,r=1;l<=n;l=r){while(r<=n && a[l]==a[r]) r++;b[++m]=r-l;}
if(m==1){for(int i=1;i<n;i++) ans.push_back(0);for(int i=1;i<n;i++) ans.push_back(3);}
else
{
for(int i=1;i<=m-2;i++)
{
for(int j=1;j<=b[i];j++) ans.push_back(0);
for(int j=1;j<b[i];j++) ans.push_back(3);
if(b[i]>1) ans.push_back(1);
ans.push_back(2);
}
for(int j=1;j<b[m-1];j++) ans.push_back(0);
for(int j=1;j<b[m-1];j++) ans.push_back(3);
ans.push_back(1);
for(int j=1;j<b[m];j++) ans.push_back(0);
for(int j=1;j<b[m];j++) ans.push_back(3);
for(int i=0;i<m-1;i++) ans.push_back(3);
}
printf("%d\n",(int)ans.size());
for(int v:ans) cout<<opt[v]<<"\n";
return 0;
}
D. Diophantine Equation
题解
因为 \(x^3+y^3=(x+y)(x^2-xy+y^2)\),考虑直接枚举 \(s=x+y\)。考虑 \(n^2\) 的因数个数,最大应该不会超过 \(5\times 10^4\),并且对 \(n\) 质因数分解可以快速进行。
然后对于每个 \(s\),可以得到 \(t=x^2-xy+y^2\) 的值,相减一下可以凑出 \((x-y)^2\),看一下这个能否解出正整数 \(x,y\) 即可。
复杂度 \(O(T\sqrt n)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int N=100010;
typedef long long ll;
bool solve(ll s,ll n)
{
ll t=n*n/s,xy=s*s-t;
if(xy%3 || xy<0) return false;
xy/=3;
if(t<xy) return false;
ll d=max(sqrt(t-xy)-1,0.0);while(d*d<t-xy) d++;
if(d*d!=t-xy) return false;
if((s+d)%2) return false;
ll x=(s+d)/2,y=(s-d)/2;
if(x<=0 || y<=0) return false;
printf("%lld %lld\n",x,y);
return true;
}
bool dfs(int x,ll s,ll n,vector<pair<int,int>>&tmp)
{
if(x==tmp.size()) return solve(s,n);
for(int c=0;c<=tmp[x].second*2 && x<=n;c++,s*=tmp[x].first)
if(dfs(x+1,s,n,tmp)) return true;
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t --> 0)
{
ll n,n0;scanf("%lld",&n),n0=n;
vector<pair<int,int>>res;
for(int i=2;i*i<=n;i++) if(n%i==0)
{
int c=0;while(n%i==0) n/=i,++c;
res.emplace_back(i,c);
}
if(n!=1) res.emplace_back(n,1);
if(!dfs(0,1,n0,res)) puts("impossible");
}
return 0;
}
F. Five-pointed Queries
题解
考虑容斥出中间部分。容易发现,总是能根据一个若干三角形以及若干小三角形拼出答案。
直接做是 \(O(nk^4)\) 的,可以用差分将一个 \(k\) 移到询问上,这样就变成 \(O((n+q)k^3)\)。事实上再移一次可以变成 \(O((n+q)k^2)\)。可以通过。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=50010,M=31;
struct node{
int x,y;node(int x=0,int y=0):x(x),y(y){}
}p[M],a[N];bool b[N];
node operator -(node a,node b){return node(a.x-b.x,a.y-b.y);}
ll operator *(node a,node b){return 1ll*a.x*b.y-1ll*a.y*b.x;}
ll cross(node a,node o,node b){return (a-o)*(b-o);}
int f[M][M][M],g[M][M][M][M],t;
int nxt(int x){return x==t?1:x+1;}
void ins(node x,int v)
{
for(int i=1;i<=t;i++)
{
int l=nxt(nxt(i));
while(l!=i && cross(x,p[i],p[l])>0) l=nxt(l);
int r=l;
for(int j=nxt(i);j!=l;j=nxt(j)) if(cross(p[j],x,p[l])<0)
{
while(r!=i && cross(p[j],x,p[r])<0) r=nxt(r);
for(int k=l;k!=r;k=nxt(k)) f[i][j][k]+=v;
for(int k=l;k!=r;k=nxt(k)) g[i][j][k][k]+=v,g[i][j][k][r]-=v;
}
}
}
int calc(vector<int>x)
{
if(x.size()==3) return f[x[0]][x[1]][x[2]];
int s=0;
for(int i=x[2];i!=x[3];i=nxt(i))
s+=g[x[0]][x[1]][x[2]][i];
s+=g[x[0]][x[1]][x[2]][x[3]];
return s;
}
int main()
{
int n,m;scanf("%d",&t);
for(int i=1;i<=t;i++) scanf("%d%d",&p[i].x,&p[i].y);
scanf("%d",&n);
for(int i=1,x;i<=n;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&x),b[i]=x;
if(x) ins(a[i],1);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int op;scanf("%d",&op);
if(op==1)
{
int x;scanf("%d",&x);
if(b[x]) ins(a[x],-1),b[x]=false;
else ins(a[x],1),b[x]=true;
}
else
{
int p[5];
for(int i=0;i<5;i++) scanf("%d",&p[i]);
int x1=0,x2=0,s=f[p[0]][p[1]][p[2]]+f[p[0]][p[2]][p[3]]+f[p[0]][p[3]][p[4]];
for(int i=0;i<5;i++) x1+=calc({p[i],p[(i+1)%5],p[(i+2)%5]});
for(int i=0;i<5;i++) x2+=calc({p[i],p[(i+1)%5],p[(i+2)%5],p[(i+4)%5]});
// cerr<<s<<" "<<x1<<" "<<x2<<endl;
printf("%d\n",s-(x1-x2));
}
}
return 0;
}
H. Hash Function
I. Infection
题解
题目要求的是条件概率,等于该条件下的所有事件中发生的概率。直接 dp 当前每个子集感染的概率,然后最后统计所有合法的子集对应概率,用总概率除以这个值即可。
复杂度 \(O(2^nm)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=15,M=60;typedef __int128 lll;
lll gcd(lll x,lll y){return y==0?x:gcd(y,x%y);}
struct frac{
lll x,y;
frac(lll x0=0,lll y0=1){lll g=gcd(x0,y0);x=x0/g,y=y0/g;}
};
void print_128(lll x){if(x>=10) print_128(x/10);putchar('0'+(x%10));}
void print(frac a){print_128(a.x);putchar('/');print_128(a.y);puts("");}
frac operator *(frac a,frac b){return frac(a.x*b.x,a.y*b.y);}
frac operator /(frac a,frac b){return frac(a.x*b.y,a.y*b.x);}
frac operator +(frac a,frac b){return frac(a.x*b.y+a.y*b.x,a.y*b.y);}
void operator +=(frac &a,frac b){a=a+b;}
int x[M],y[M];
frac f[M][1<<N],ans[N];
int main()
{
int n,m,k;scanf("%d%d%d",&n,&k,&m),--k;
for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),--x[i],--y[i];
for(int i=0;i<n;i++) f[0][1<<i]=frac(1,n);
for(int i=1;i<=m;i++)
for(int s=0;s<1<<n;s++)
{
int c=(s>>x[i]&1)|(s>>y[i]&1);
f[i][s|(c<<x[i])|(c<<y[i])]+=f[i-1][s]/2,f[i][s]+=f[i-1][s]/2;
}
frac all=0;
for(int s=0;s<1<<n;s++) if(s>>k&1)
{
all+=f[m][s];
for(int i=0;i<n;i++) if(s>>i&1) ans[i]+=f[m][s];
}
for(int i=0;i<n;i++) print(ans[i]/all);
return 0;
}
J. Jumping Path
题解
考虑一定存在一种最优方案,使得任意一段极长的连续跳跃的起始点或者终止点是某个禁入区域的端点或者起点终点。
容易发现,如果一次跳跃中间不存在任何禁入点,这次跳跃一定是不优的。所以任意一段极长连续跳跃长度不会超过 \(n\)。直接对每个禁入区域的端点和从这个点出发可以连续跳跃到达的合法点设为关键点,对关键点之间做 dijkstra 即可。
复杂度 \(O(n^2\log n)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=1010,M=1000010;
typedef double db;
const db inf=1e18,pi=acos(-1);
int a[N],r0,b[N],p[M],n,m;db dis[M];bool vis[M];
bool in(int x,int p){return x>a[p]-r0 && x<a[p]+r0;}
bool out(int l,int r){return *lower_bound(a+1,a+n+1,l)>=r;}
int pos(int x){int q=lower_bound(p+1,p+m+1,x)-p;return p[q]==x?q:-1;}
int main()
{
int R,A,B,tt=0;scanf("%d%d%d%d%d",&n,&r0,&R,&A,&B),R*=2;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1),n=unique(a+1,a+n+1)-a-1;
for(int i=1;i<=n;i++)
{
bool hv=false;
for(int j=1;j<=n;j++) if(in(a[i]+r0,j)){hv=true;break;}
if(!hv) b[++tt]=a[i]+r0;hv=false;
for(int j=1;j<=n;j++) if(in(a[i]-r0,j)){hv=true;break;}
if(!hv) b[++tt]=a[i]-r0;
}
a[0]=-2e9,a[n+1]=2e9;
b[++tt]=A,b[++tt]=B;
sort(b+1,b+tt+1),tt=unique(b+1,b+tt+1)-b-1;
for(int i=1;i<=tt;i++)
{
int v=b[i],u=1;
while(u<=n && a[u]<=v) ++u;
while(true)
{
p[++m]=v,v+=R;
if(v<a[u]) break;
while(u<=n && a[u]<=v) ++u;
if(in(v,u-1) || in(v,u)) break;
}
v=b[i],u=n;
while(u && a[u]>=v) --u;
while(true)
{
p[++m]=v,v-=R;
if(v>a[u]) break;
while(u && a[u]>=v) --u;
if(in(v,u+1) || in(v,u)) break;
}
}
sort(p+1,p+m+1),m=unique(p+1,p+m+1)-p-1;
// for(int i=1;i<=m;i++) cerr<<p[i]<<" ";cerr<<endl;
for(int i=1;i<=m;i++) dis[i]=inf;
priority_queue<pair<db,int>,vector<pair<db,int>>,greater<pair<db,int>>>q;
auto push=[&](int u,db d){if(dis[u]>d) dis[u]=d,q.emplace(d,u);};
push(pos(A),0);
while(!q.empty())
{
int u=q.top().second;q.pop();
if(vis[u]) continue;
vis[u]=true;
if(u>1 && out(p[u-1],p[u])) push(u-1,dis[u]+(p[u]-p[u-1]));
if(u<m && out(p[u],p[u+1])) push(u+1,dis[u]+(p[u+1]-p[u]));
int v=pos(p[u]+R);if(v!=-1) push(v,dis[u]+pi/2*R);
v=pos(p[u]-R);if(v!=-1) push(v,dis[u]+pi/2*R);
}
db ans=dis[pos(B)];
if(ans>=inf) puts("-1");
else printf("%.7lf\n",ans);
return 0;
}

浙公网安备 33010602011771号