DongDong坐飞机
题解:分层图最短路,dis[ i ][ j ]记录下到达 i点 j次打折的最短路
然后Dijkstra就可解了。
注意:
1.因为是分层的所以,一个节点会多次遍历所以不必标记
2.输入输出用快读,或者scanf,居然显示的数据错误,而不是超时…卡在80%
AC代码
#include<bits/stdc++.h>
#include<queue>
#define int long long
#define INF 1e18
using namespace std;
const int maxn=20010;
typedef long long ll;
inline int read(){
int w=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
w=(w<<3)+(w<<1)+ch-48;
ch=getchar();
}
return w*f;
}
ll dis[maxn][12],value[maxn];
int vis[maxn];//不需要记录是否询问,因为需要重复访问
struct edge
{
ll from,to,val,pen;
}h;
struct cmp{
bool operator()(const edge &a,const edge &b)
{
return a.val>b.val;
}
};
signed main(){
ll m,n,s,t,k;
m=read();n=read();k=read();
vector<edge>E[maxn];
priority_queue<edge,vector<edge>,cmp>Q;
for(int i=0;i<=20000;i++)
for(int j=0;j<=10;j++)
dis[i][j]=INF;
for(int i=0;i<n;i++)
{
ll u,v,w,p;
u=read(),v=read(),w=read();
h.from=u,h.to=v,h.val=w;
E[u].push_back(h);
}
s=1,t=m;
dis[s][0]=0;
h.from=s,h.to=s,h.val=0,h.pen=0;
Q.push(h);
while(!Q.empty())
{
edge x=Q.top();
Q.pop();
if(x.pen>k) continue;
if(x.val>dis[x.to][x.pen]) continue;
int sz=E[x.to].size();
for(int i=0;i<sz;i++)
{
edge y=E[x.to][i];
if(dis[y.to][x.pen]>dis[x.to][x.pen]+y.val)
{
dis[y.to][x.pen]=dis[x.to][x.pen]+y.val;
h.from=x.to,h.to=y.to,h.val=dis[y.to][x.pen],h.pen=x.pen;
Q.push(h);
}
if(dis[y.to][x.pen+1]>dis[x.to][x.pen]+y.val/2)
{
dis[y.to][x.pen+1]=dis[x.to][x.pen]+y.val/2;
h.from=x.to,h.to=y.to,h.val=dis[y.to][x.pen+1],h.pen=x.pen+1;
Q.push(h);
}
}
}
ll ans=INF;
for(int i=0;i<=k;i++){
//cout<<dis[t][i]<<endl;
ans=min(ans,dis[t][i]);
}
if(ans==INF) cout<<-1<<endl;
else cout<<ans<<endl;
}
DongDong数颜色
题解:
做法1:dfs序+莫队,先把树上的点标上dfs序,因为子树的dfs序是连续的,所以子树可以表示为id[x]到id[x]+size[x]+1id[x]到id[x]+size[x]+1,然后就是序列上莫队了
做法2:DSU ON TREE,对于每个节点的子树开一颗线段树或者set,然后不断向上合并即可
做法3:考虑每个节点的贡献,点u对上一个颜色与点u相同的点的LCA贡献-1,u对自身贡献1,每个节点的ans就是子树贡献和,然后上个树状数组或者线段树维护一下就好。
莫队+dfs序 代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+10;
int inx[maxn];
int out[maxn];
int n,k;
int color[maxn];
int c[maxn];
vector<int>G[maxn];
int cnt;
int vis[maxn];
void dfs(int u){///dfs序
inx[u]=++cnt;
vis[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(!vis[v])
dfs(v);
}
out[u]=cnt;
}
//莫队离线询问
struct node{
int l,r,id;
}Q[maxn];
int pos[maxn];//保存所在块
bool cmp(const node &a,const node &b)
{
if(pos[a.l]==pos[b.l])
return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int flag[maxn];//数字在当前区间出现次数
int Ans;
int ans[maxn];
void add(int x){ flag[c[x]]++; if(flag[c[x]]==1) Ans++;}
void del(int x){ flag[c[x]]--; if(flag[c[x]]==0) Ans--;}
int main(){
scanf("%d%d",&n,&k);
cnt=0;
int sz=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&color[i]);
pos[i]=i/sz;
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
memset(vis,0,sizeof(vis));
memset(out,0,sizeof(out));
memset(inx,0,sizeof(inx));
memset(flag,0,sizeof(flag));
///以上都是初始化
dfs(1);
for(int i=1;i<=n;i++){
c[inx[i]]=color[i];
}
Ans=0;
for(int i=1;i<=k;i++){
int u;
scanf("%d",&u);
int ll=inx[u],rr=out[u];
Q[i].l=ll;
Q[i].r=rr;
Q[i].id=i;
}
sort(Q+1,Q+k+1,cmp);///莫队排序
int L=1,R=0;
for(int i=1;i<=k;i++)
{
while(R<Q[i].r){R++;add(R);}
while(L>Q[i].l){L--;add(L);}
while(L<Q[i].l){del(L);L++;}
while(R>Q[i].r){del(R);R--;}
ans[Q[i].id]=Ans;
}
for(int i=1;i<=k;i++)
printf("%d\n",ans[i]);
return 0;
}
D. GCD Table
题意:
给定n*m的矩阵,[i,j]的点值为gcd(i,j)
给定一个k长的序列,问是否能匹配上 矩阵的某一行的连续k个元素
题解:中国剩余定理
注意:数字比较大,需要用快速乘优化一下
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=10005;
LL k;
LL c[N],m[N],mp[N];///mp为m的副本
bool flag;
LL mul(LL a,LL p,LL Mod)///快速乘
{
LL ans=0LL,f;
if (p<0) a=-a,p=-p;
for (;p;p>>=1LL,a=(a+a)%Mod)
if (p&1LL)
ans=(ans+a)%Mod;
return ans;
}
LL gcd(LL a,LL b)
{
return b?gcd(b,a%b):a;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b) {x=1;y=0;return a;}
else exgcd(b,a%b,y,x),y-=a/b*x;
}
LL inv(LL a,LL mod)
{
LL x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
LL exCRT(LL k)///中国剩余定理
{
LL m1,m2,c1,c2,g;
for(LL i=2;i<=k;i++)
{
m1=m[i-1],m2=m[i];
c1=c[i-1],c2=c[i];
g=gcd(m1,m2);
if((c2-c1)%g) {flag=0;return -1;}
m[i]=m1*m2/g;
///c[i]=inv(m1/g,m2/g)*((c2-c1)/g)%(m2/g)*m1+c1;
///数字太大乘法超long long 应用大数乘法
c[i]=inv(m1/g,m2/g);
c[i]=mul(c[i],((c2-c1)/g),(m2/g));
c[i]=mul(c[i],m1,m[i])+c1;
c[i]=(c[i]%m[i]+m[i])%m[i];
}
return c[k];
}
int main()
{
LL n,m1;
scanf("%lld%lld%lld",&n,&m1,&k);
LL lcm=1;
for(LL i=1;i<=k;i++) scanf("%lld",&m[i]),c[i]=m[i]-i+1LL,mp[i]=m[i];
lcm=m[1];
for (LL i=2;i<=k;++i)
{
LL t=__gcd(lcm,m[i]);
lcm=lcm/t*m[i];
if (lcm>n) {printf("NO\n");return 0;}
}
LL ans=exCRT(k);
//cout<<ans<<endl;
if(ans==0) ans+=m[k];///找到不为0的最小符合题意的数,因为数的范围为[1,
if(ans+k-1>m1 || ans<0){printf("NO\n");return 0;}
for(LL i=1;i<=k;i++)
{
//cout<<__gcd(lcm,ans+i-1)<<" "<<m[i]<<endl;
if(__gcd(lcm,ans+i-1LL)!=mp[i]){printf("NO\n");return 0;}
}
printf("YES\n");
}