夕阳红选手就会做俩签到题,剩下的日后再补。
题目描述
uoj:https://www.lydsy.com/JudgeOnline/problem.php?id=5415
log:https://loj.ac/problem/2718
题解
看到这题先yy了一个傻逼做法,吧所有边按海拔从大到小跑一遍可持久化并查集,并且维护个距离最下值。在询问的时候在对应的点找到对应的可持久化并查集版本查询就好了。
后来发现了一种更优秀的做法,我们把这些边按照海拔从大到小建一课kruskal重构树,每个点维护下子树的最大值。每次询问等于查询一个子树的最大值,先倍增一下再求个最大值就好了。
//现在OI题是都会卡spfa的吗,改了dij才过的。
代码
#include
using namespace std;
#define N 400010
#define D 19
using namespace std;
int T,n,m,pre,Q,K,S,fa[N],f[N],g[N];
int k,la[N],ff[N*2],w[N][D+1],cnt;
struct node{
int a,b,c,d;
bool operator<(const node &p)const{return d>p.d;}
}e[N*2];
struct info{
int a,b;
bool operator<(const info &p)const{return b>p.b;}
};
priority_queueq;
void add(int a,int b,int c,int d)
{
e[++k]=(node){a,b,c,d};ff[k]=la[a];la[a]=k;
e[++k]=(node){b,a,c,d};ff[k]=la[b];la[b]=k;
}
inline int read()
{
char ch;int v;
while(!isdigit(ch=getchar()));v=ch-48;
while(isdigit(ch=getchar()))v=v*10+ch-48;
return v;
}
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
inline void dij()
{
memset(f,63,sizeof(f));
q.push((info){1,f[1]=0});
while(!q.empty())
{
info p=q.top();q.pop();
if(f[p.a]!=p.b)continue;
int x=p.a;
for(int a=la[x];a;a=ff[a])
if(f[e[a].b]>f[x]+e[a].c)
{
f[e[a].b]=f[x]+e[a].c;
q.push((info){e[a].b,f[e[a].b]});
}
}
}
void prepare()
{
w[cnt][0]=cnt;
for(int j=1;j<=D;j++)
for(int i=1;i<=cnt;i++)
w[i][j]=w[w[i][j-1]][j-1];
}
int solve(int x,int y)
{
for(int i=D;i>=0;i--)
if(g[w[x][i]]>y)x=w[x][i];
return x;
}
int main()
{
int a,b,c,d;
T=read();
while(T--)
{
n=read();m=read();cnt=n;k=0;pre=0;
for(int i=1;i<=n;i++)fa[i]=i;
memset(la,0,sizeof(la));
memset(ff,0,sizeof(ff));
memset(w,0,sizeof(w));
memset(g,0,sizeof(g));
for(int i=1;i<=m;i++)
a=read(),b=read(),c=read(),d=read(),add(a,b,c,d);
dij();sort(e+1,e+m*2+1);
for(int i=1;i<=m*2;++i)
{
a=find(e[i].a);b=find(e[i].b);
if(a==b)continue;
w[a][0]=w[b][0]=++cnt;fa[a]=cnt;fa[b]=cnt;
fa[cnt]=cnt;g[cnt]=e[i].d;f[cnt]=min(f[a],f[b]);
}
prepare();
Q=read();K=read();S=read();
while(Q--)
{
a=read();b=read();
a=(a+K*pre-1)%n+1;b=(b+K*pre)%(S+1);
printf("%d\n",pre=f[solve(a,b)]);
}
}
return 0;
}
题目描述
uoj:https://www.lydsy.com/JudgeOnline/problem.php?id=5418
log:https://loj.ac/problem/2721
题解
先用set把每次攻击对应的剑找到,然后这题就变成了给你n个同余方程求解。
大概就是 t[i]*x==a[i](mod p[i])
如果模数两两互质的话直接上CRT就好了,不互质的话就先每个用exgcd求解然后再两两合并。
至于合并,考虑前n个方程得到的解 x==X(mod D),即x=k1*D+X。
当前同余方程的解,x=k2*d+x0,所以 k1*D+X=k2*d+x0,移个项就是同余方程,上exgcd搞搞就可以了。
无解的话就是任意一个同余方程无解。
代码
#include
#define ll long long
#define N 100010
using namespace std;
int T,n,m;ll a[N],p[N],s[N],t[N];
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x=1;y=0;return;}
exgcd(b,a%b,y,x);y-=x*(a/b);
}
multiset q;
multiset ::iterator it;
inline ll mul(ll a,ll b,ll n)
{
ll res=0;a%=n;
while(b)
{
if(b&1)res=(res+a)%n;
a=(a<<1)%n;b/=2;
}
return (res+n)%n;
}
int main()
{
//freopen("dragon.in","r",stdin);
//freopen("dragon.out","w",stdout);
ll x;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);q.clear();
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
for(int i=1;i<=m;i++)scanf("%lld",&x),q.insert(x);
for(int i=1;i<=n;i++)
{
it=q.upper_bound(a[i]);
if(it!=q.begin())--it;
t[i]=*it;q.erase(it);q.insert(s[i]);
}
//同余方程:t[i]*x==a[i](mod p[i])
int flag=0;ll X,D;
for(int i=1;i<=n;i++)
{
ll tmp=__gcd(t[i],p[i]),x0,y0,d,x,y;
if(a[i]%tmp){flag=1;break;}
exgcd(t[i]/tmp,d=p[i]/tmp,x0,y0);
x0=mul(x0,a[i]/tmp,d);if(!x0)x0=d;
y0=(a[i]-t[i]*x0)/p[i];
if(y0>0)
{
ll g=t[i]/tmp;
ll bs=((g+(-y0)%g)%g+y0)/g;
x0+=bs*d;
}
//解同余方程 当前方程解 x=x0+k*d
if(i==1){X=x0;D=d;continue;}
if(x0if((x0-X)%tmp){flag=1;break;}
exgcd(D/tmp,d/tmp,x,y);
x=mul(x,(x0-X)/tmp,d/tmp);y=(x0-X-D*x)/d;
if(y>0)
{
ll g=D/tmp;
ll num=((-y)%g+g)%g;
ll bs=(num+y)/tmp;
x+=bs*d/tmp;y=num;
}
X=x0-d*y;D=D/tmp*d;
//合并同于方程 得到解 x=X+k*D
}
if(flag)printf("-1\n");
else printf("%lld\n",X);
}
return 0;
}