NOIP提高组1570~1580集合答案

1870:【12NOIP提高组】国王游戏

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

【输入】
第一行包含一个整数 n,表示大臣的人数。

第二行包含两个整数 a 和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

【输出】
输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

【输入样例】
3
1 1
2 3
7 4
4 6
【输出样例】
2
【提示】
【输入输出样例说明】

按 1、2、3 号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 1、3、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 2、1、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 2、3、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;

按 3、1、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 3、2、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9。

因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2。

【数据范围】

对于 20%的数据,有 1≤ n≤ 10,0 < a、b < 8;

对于 40%的数据,有 1≤ n≤20,0 < a、b < 8;

对于 60%的数据,有 1≤ n≤100;

对于 60%的数据,保证答案不超过 109;

对于 100%的数据,有 1 ≤ n ≤1,000,0 < a、b < 10000。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
using namespace std;
typedef int arr[6005];
struct note{
    ll a,b,c;
}a[1005];
bool cmp(note x,note y) {
    return x.c<y.c;
}
int n,bz;
arr sum,ans,t;
char s[5];
void divv(int x){
    int yu=0;memset(t,0,sizeof(t));
    fd(i,sum[0],1) {
        yu=yu*10+sum[i];
        if (yu>=x) {
            if (!t[0]) t[0]=i;
            t[i]=yu/x;yu%=x;
        }
    }
}
void mx() {
    if (t[0]>ans[0]) memcpy(ans,t,sizeof(ans));
    else if (ans[0]==t[0]) {
        fd(i,ans[0],1) if (t[i]>ans[i]) {
            memcpy(ans,t,sizeof(ans));return;
        } else if (ans[i]<t[i]) return;
    }
}
void cheng(int x) {
    arr t;memset(t,0,sizeof(t));
    fo(i,1,sum[0]) {
        t[i]=t[i]+sum[i]*x;t[i+1]+=t[i]/10;t[i]%=10;
    }
    for(t[0]=sum[0];t[t[0]+1];) t[++t[0]+1]+=t[t[0]]/10,t[t[0]]%=10;
    memcpy(sum,t,sizeof(sum));
}
int main() {
    scanf("%d",&n);scanf("%s",s+1);
    fd(i,strlen(s+1),1) sum[++sum[0]]=s[i]-'0';scanf("%d",&bz);
    fo(i,1,n) scanf("%lld%lld",&a[i].a,&a[i].b),a[i].c=a[i].a*a[i].b;
    sort(a+1,a+n+1,cmp);
    fo(i,1,n) {
        divv(a[i].b);mx();cheng(a[i].a);
    }
    fd(i,ans[0],1) printf("%d",ans[i]);
}
1871:【12NOIP提高组】开车旅行

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 199     通过数: 104 

【题目描述】
小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j]=︱Hi-Hj︱。

【输入】
第一行包含一个整数 N,表示城市的数目。

第二行有 N 个整数,每两个整数之间用一个空格隔开,依次表示城市 1 到城市 N 的海拔高度,即 H1,H2,……,Hn,且每个 Hi 都是不同的。

第三行包含一个整数 X0。

第四行为一个整数 M,表示给定 M 组 Si 和 Xi。

接下来的 M 行,每行包含 2 个整数 Si 和 Xi,表示从城市 Si 出发,最多行驶 Xi 公里。

【输出】
输出共 M+1 行。

第一行包含一个整数 S0,表示对于给定的 X0,从编号为 S0 的城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小。

接下来的 M 行,每行包含 2 个整数,之间用一个空格隔开,依次表示在给定的 Si 和Xi 下小 A 行驶的里程总数和小 B 行驶的里程总数。

【输入样例】
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3
【输出样例】
1
1 1
2 0
0 0
0 0


【提示】
【输入输出样例 1 说明】



各个城市的海拔高度以及两个城市间的距离如上图所示。

如果从城市 1 出发,可以到达的城市为 2,3,4,这几个城市与城市 1 的距离分别为 1,1,2,但是由于城市 3 的海拔高度低于城市 2,所以我们认为城市 3 离城市 1 最近,城市 2 离城市1 第二近,所以小 A 会走到城市 2。到达城市 2 后,前面可以到达的城市为 3,4,这两个城市与城市 2 的距离分别为 2,1,所以城市 4 离城市 2 最近,因此小 B 会走到城市 4。到达城

市 4 后,前面已没有可到达的城市,所以旅行结束。

如果从城市 2 出发,可以到达的城市为 3,4,这两个城市与城市 2 的距离分别为 2,1,由于城市 3 离城市 2 第二近,所以小 A 会走到城市 3。到达城市 3 后,前面尚未旅行的城市为4,所以城市 4 离城市 3 最近,但是如果要到达城市 4,则总路程为 2+3=5>3,所以小 B 会直接在城市 3 结束旅行。

如果从城市 3 出发,可以到达的城市为 4,由于没有离城市 3 第二近的城市,因此旅行还未开始就结束了。

如果从城市 4 出发,没有可以到达的城市,因此旅行还未开始就结束了。

【输入输出样例 2】

输入:

10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7
输出:

2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
【输入输出样例 2 说明】

当 X=7 时,如果从城市 1 出发,则路线为 1 -> 2 -> 3 -> 8 -> 9,小 A 走的距离为 1+2=3,小 B 走的距离为 1+1=2。(在城市 1 时,距离小 A 最近的城市是 2 和 6,但是城市 2 的海拔更高,视为与城市 1 第二近的城市,所以小 A 最终选择城市 2;走到 9 后,小 A 只有城市 10 可以走,没有第 2 选择可以选,所以没法做出选择,结束旅行)

如果从城市 2 出发,则路线为 2 -> 6 -> 7 ,小 A 和小 B 走的距离分别为 2,4。

如果从城市 3 出发,则路线为 3 -> 8 -> 9,小 A 和小 B 走的距离分别为 2,1。

如果从城市 4 出发,则路线为 4 -> 6 -> 7,小 A 和小 B 走的距离分别为 2,4。

如果从城市 5 出发,则路线为 5 -> 7 -> 8 ,小 A 和小 B 走的距离分别为 5,1。

如果从城市 6 出发,则路线为 6 -> 8 -> 9,小 A 和小 B 走的距离分别为 5,1。

如果从城市 7 出发,则路线为 7 -> 9 -> 10,小 A 和小 B 走的距离分别为 2,1。

如果从城市 8 出发,则路线为 8 -> 10,小 A 和小 B 走的距离分别为 2,0。

如果从城市 9 出发,则路线为 9,小 A 和小 B 走的距离分别为 0,0(旅行一开始就结束了)。

如果从城市 10 出发,则路线为 10,小 A 和小 B 走的距离分别为 0,0。

从城市 2 或者城市 4 出发小 A 行驶的路程总数与小 B 行驶的路程总数的比值都最小,但是城市 2 的海拔更高,所以输出第一行为 2。

【数据范围】

对于 30%的数据,有 1≤N≤20,1≤M≤201≤N≤20,1≤M≤20;

对于 40%的数据,有 1≤N≤100,1≤M≤1001≤N≤100,1≤M≤100;

对于 50%的数据,有 1≤N≤100,1≤M≤1,0001≤N≤100,1≤M≤1,000;

对于 70%的数据,有 1≤N≤1,000,1≤M≤10,0001≤N≤1,000,1≤M≤10,000;

对于 100%的数据,有 1≤N≤100,000,1≤M≤10,000,−1,000,000,000≤Hi≤1,000,000,000,0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,0001≤N≤100,000,1≤M≤10,000,−1,000,000,000≤Hi≤1,000,000,000,0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,数据保证 HiHi 互不相同。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define db double
#define ll long long
#define inf 0x7fffffff
using namespace std;
struct node{
    int d;ll v;
}p[N];
struct note{
    int mx,mi;
}t[N*5];
bool cmp(node x,node y) {
    return x.v<y.v||x.v==y.v&&x.d<y.d;
}
db sum,ans;
int n,m,tot,x,y,k;
int h[N],a[N],b[N],w[N],g[N][18];
ll f[N][18][2],ana,anb,v[N];
void change(int v,int l,int r,int x) {
    if (l==r) {t[v].mx=t[v].mi=l;return;}
    int m=(l+r)/2;
    if (x<=m) change(v*2,l,m,x);
    else change(v*2+1,m+1,r,x);
    t[v].mx=max(t[v*2].mx,t[v*2+1].mx);
    t[v].mi=min(t[v*2].mi,t[v*2+1].mi);
}
int getmx(int v,int l,int r,int x,int y) {
    if (x>y) return 0;
    if (l==x&&r==y) return t[v].mx;
    int m=(l+r)/2;
    if (y<=m) return getmx(v*2,l,m,x,y);
    else if (x>m) return getmx(v*2+1,m+1,r,x,y);
    else return max(getmx(v*2,l,m,x,m),getmx(v*2+1,m+1,r,m+1,y));
}int getmi(int v,int l,int r,int x,int y) {
    if (x>y) return n+1;
    if (l==x&&r==y) return t[v].mi;
    int m=(l+r)/2;
    if (y<=m) return getmi(v*2,l,m,x,y);
    else if (x>m) return getmi(v*2+1,m+1,r,x,y);
    else return min(getmi(v*2,l,m,x,m),getmi(v*2+1,m+1,r,m+1,y));
}
void solve(int x,int y) {
    ana=anb=0;
    fd(j,17,0) 
        if (f[x][j][0]+f[x][j][1]<=y) {
            y-=f[x][j][0]+f[x][j][1];
            ana+=f[x][j][0];anb+=f[x][j][1];
            x=g[x][j];
        } 
    if (f[x][0][0]<=y) ana+=f[x][0][0];
}
int main() {
    scanf("%d",&n);
    fo(i,1,n) scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i;
    sort(p+1,p+n+1,cmp);v[0]=inf;
    fo(i,1,n) h[p[i].d]=++tot,w[tot]=p[i].d;
    fo(i,1,n*5) t[i].mi=n+1;
    fd(i,n,1) {
        p[1].d=getmi(1,1,n,h[i]+1,n);p[2].d=getmx(1,1,n,1,h[i]-1);
        p[3].d=getmi(1,1,n,p[1].d+1,n);p[4].d=getmx(1,1,n,1,p[2].d-1);
        fo(j,1,4) p[j].v=abs(v[i]-v[w[p[j].d]]);    
        sort(p+1,p+5,cmp);
        if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d];
        if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d];
        change(1,1,n,h[i]);         
    }
    fo(i,1,n) {
        g[i][0]=b[a[i]];
        f[i][0][0]=abs(v[i]-v[a[i]]);
        f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]);
    }
    fo(j,1,17)
        fo(i,1,n) {
            g[i][j]=g[g[i][j-1]][j-1];
            f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
            f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
        }
    scanf("%d",&x);ans=inf;
    fo(i,1,n) {
        solve(i,x);
        if (!anb) sum=inf;else sum=ana*1.0/anb;
        if (sum<ans||sum==ans&&v[i]>v[k]) ans=sum,k=i;
    }
    printf("%d\n",k);
    for(scanf("%d",&m);m;m--) {
        scanf("%d%d",&x,&y);
        solve(x,y);
        printf("%lld %lld\n",ana,anb);  
    }
}
1872:【12NOIP提高组】同余方程

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 678     通过数: 346 

【题目描述】
求关于 xx 的同余方程 ax≡1(modb)ax≡1(modb)的最小正整数解。

【输入】
输入只有一行,包含两个正整数 a,ba,b,用一个空格隔开。

【输出】
输出只有一行,包含一个正整数 x0x0,即最小正整数解。输入数据保证一定有解。

【输入样例】
3 10
【输出样例】
7
【提示】
【数据范围】

对于 40%的数据,2≤b≤1,0002≤b≤1,000;

对于 60%的数据,2≤b≤50,000,0002≤b≤50,000,000;

对于 100%的数据,2≤a,b≤2,000,000,0002≤a,b≤2,000,000,000。
#include<cstdio>
#define ll long long
long long a,b,x,y;
void exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b) x=1,y=0;
	else
	{
		exgcd(b,a%b,x,y);
		int t;
		t=x;x=y;y=t-a/b*y;
	}
}
int main()
{
	long long a,b,x,y;
	scanf("%lld%lld",&a,&b);
	exgcd(a,b,x,y);
	printf("%lld",(x+b)%b);
}
1873:【12NOIP提高组】借教室

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 880     通过数: 363 

【题目描述】
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj, sj, tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

【输入】
第一行包含两个正整数n, m,表示天数和订单的数量。

第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。

接下来有m行,每行包含三个正整数dj, sj, tj,表示租借的数量,租借开始、结束分别在第几天。

每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。

【输出】
如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足)

输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

【输入样例】
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
【输出样例】
-1
2

【提示】
【输入输出样例说明】

第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第2 个申请人修改订单。

【数据范围】

对于 10%的数据,有1 ≤ n, m ≤ 10;

对于 30%的数据,有1 ≤ n, m ≤ 1000;

对于 70%的数据,有1 ≤ n, m ≤ 105;

对于 100%的数据,有1 ≤ n, m ≤ 106, 0 ≤ ri, dj ≤ 109, 1 ≤ sj ≤ tj ≤ n。
#include <bits/stdc++.h>
using namespace std;
 
const int Max=1000010;
int n,m;
int num[Max];
int tree[Max<<2],sub[Max<<2];
 
inline int get_int()
{
	int x=0,f=1;
	char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') f=-1,c=getchar();
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	return x*f;
}
 
inline int mn(int x,int y){return x < y ? x : y;}
inline void update(int root){tree[root]=min(tree[root<<1],tree[root<<1|1]);}
inline void pushdown(int root)
{
	tree[root<<1]+=sub[root],tree[root<<1|1]+=sub[root];
	sub[root<<1]+=sub[root],sub[root<<1|1]+=sub[root];
	sub[root]=0;
}
inline void build(int root,int l,int r)
{
	if(l==r) {tree[root]=num[l];return;}
	int mid=(l+r)>>1;
	build(root<<1,l,mid),build(root<<1|1,mid+1,r);
	update(root);
}
inline void add(int root,int l,int r,int L,int R,int x)
{
	if(L<=l&&R>=r) {tree[root]+=x,sub[root]+=x;return;}
	int mid=(l+r>>1);
	pushdown(root);
	if(L<=mid) add(root<<1,l,mid,L,R,x);
	if(R>mid) add(root<<1|1,mid+1,r,L,R,x);
	update(root);
}
inline int Q(int root,int l,int r,int L,int R)
{
	if(L<=l&&R>=r) return tree[root];
	int mid=(l+r)>>1,ans=1e9+7;
	pushdown(root);
	if(L<=mid) ans=min(ans,Q(root<<1,l,mid,L,R));
	if(R>mid) ans=min(ans,Q(root<<1|1,mid+1,r,L,R));
	return ans;
}
 
int main()
{
	n=get_int(),m=get_int();
	for(int i=1;i<=n;i++) num[i]=get_int();
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
	  int x=get_int(),l=get_int(),r=get_int();
	  if(Q(1,1,n,l,r)>=x) add(1,1,n,l,r,-x);
	  else {cout<<"-1\n"<<i<<"\n";return 0;}
	}
	cout<<"0\n";
	return 0;
}
1874:【12NOIP提高组】疫情控制

时间限制: 2000 ms         内存限制: 131072 KB
提交数: 322     通过数: 204 

【题目描述】
H 国有 nn 个城市,这 nn 个城市用 n−1n−1 条双向道路相互连通构成一棵树,11 号城市是首都,也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。

请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

【输入】
第一行一个整数 nn,表示城市个数。

接下来的 n−1n−1 行,每行 33 个整数,u、v、wu、v、w,每两个整数之间用一个空格隔开,表示从城市 uu 到城市 vv 有一条长为 ww 的道路。数据保证输入的是一棵树,且根节点编号为 11。

接下来一行一个整数 mm,表示军队个数。

接下来一行 mm 个整数,每两个整数之间用一个空格隔开,分别表示这 mm 个军队所驻扎的城市的编号。

【输出】
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出−1−1。

【输入样例】
4
1 2 1
1 3 2
3 4 3
2
2 2
【输出样例】
3
【提示】
【输入输出样例说明】

第一支军队在 22 号点设立检查点,第二支军队从 22 号点移动到 33 号点设立检查点,所需时间为 33 个小时。

【数据范围】

保证军队不会驻扎在首都。

对于 20%的数据,2≤n≤102≤n≤10;

对于 40%的数据,2≤n≤50,0<w<1052≤n≤50,0<w<105;

对于 60%的数据,2≤n≤1000,0<w<1062≤n≤1000,0<w<106;

对于 80%的数据,2≤n≤10,0002≤n≤10,000;

对于 100%的数据,2≤m≤n≤50,000,0<w<1092≤m≤n≤50,000,0<w<109。
#include <bits/stdc++.h>
using namespace std;
 
const int Max=50005;
int n,m,size,l,r,mid,tot1,tot2;
int first[Max],num[Max],f[Max][18],d[Max][18],p[Max],vis[Max];
struct bian{int to,next,len;};
bian edge[Max<<1];
struct shu{int id,len;};
shu a[Max],b[Max];
 
inline int get_int()
{
	int x=0,f=1;
	char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') f=-1,c=getchar();
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	return x*f;
}
 
inline void build(int x,int y,int z)
{
	edge[++size].next=first[x];
	first[x]=size;
	edge[size].to=y,edge[size].len=z;
}
 
inline void dfs(int point,int fa)
{
	for(int u=first[point];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(to==fa) continue;
	  f[to][0]=point,d[to][0]=edge[u].len;
	  dfs(to,point);
	}
}
 
inline void pre()
{
	for(int j=1;j<=16;j++)
	   for(int i=1;i<=n;i++)
	     if(f[i][j-1]) f[i][j]=f[f[i][j-1]][j-1],d[i][j]=d[i][j-1]+d[f[i][j-1]][j-1];
}
 
inline void search(int point,int fa)
{
	if(vis[point]) return;
	vis[point]=1;int tag=0;
	for(int u=first[point];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(to==fa) continue;
	  tag=1;
	  search(to,point);
	  vis[point]&=vis[to];
	}
	if(!tag) vis[point]=0;  //叶子节点需特判
}
 
inline bool comp(const shu &a,const shu &b){return a.len<b.len;}
inline bool check(int mid)
{
	tot1=tot2=0;
	for(int i=1;i<=n;i++) vis[i]=0;
	for(int i=1;i<=m;i++)
	{
	  int sum=0,x=p[i];
	  for(int k=16;k>=0;k--)
	  	if(f[x][k]>1&&sum+d[x][k]<=mid) sum+=d[x][k],x=f[x][k];
	  if(f[x][0]==1&&sum+d[x][0]<=mid) a[++tot1].len=mid-sum-d[x][0],a[tot1].id=x;
	  else vis[x]=1;
	}
	search(1,0);
	if(vis[1]) return 1;
	for(int u=first[1];u;u=edge[u].next)
	  if(!vis[edge[u].to]) b[++tot2].len=edge[u].len,b[tot2].id=edge[u].to;
	sort(a+1,a+tot1+1,comp),sort(b+1,b+tot2+1,comp);
	if(tot1<tot2) return 0;
    int tag=1;
    for(int i=1;i<=tot1;i++)
    {
      if(!vis[a[i].id]) vis[a[i].id]=1;   //题解中的优先覆盖
      else if(a[i].len>=b[tag].len) vis[b[tag].id]=1;
      while(tag<=tot2&&vis[b[tag].id]) tag++;
      if(tag>tot2) return 1;
    }
    return tag>tot2;
}
 
int main()
{
	n=get_int();
	for(int i=1;i<n;i++)
	{
	  int x=get_int(),y=get_int(),z=get_int();
	  build(x,y,z),build(y,x,z);
	}
	m=get_int();
	for(int i=1;i<=m;i++) p[i]=get_int();
	dfs(1,0);
	pre();
	l=0,r=1e9;int tag=0;
	while(l<r)
	{
	  mid=(l+r)>>1;
	  if(check(mid)) tag=1,r=mid;
	  else l=mid+1;
	}
	if(tag) cout<<r<<"\n";
	else cout<<"-1\n";
	return 0; 
}
 
 

1875:【13NOIP提高组】转圈游戏

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 1306     通过数: 593 

【题目描述】
n个小伙伴(编号从0到n-1)围坐一圈玩游戏。按照顺时针方向给n个位置编号,从0到n-1。最初,第0号小伙伴在第0号位置,第 1 号小伙伴在第 1 号位置,……,依此类推。 

游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类推,第n − m号位置上的小伙伴走到第 0 号位置,第n-m+1 号位置上的小伙伴走到第 1 号位置,……,第 n-1 号位置上的小伙伴顺时针走到第m-1 号位置。 

现在,一共进行了 10k轮,请问x号小伙伴最后走到了第几号位置。

【输入】
输入共 1 行,包含 4 个整数 n、m、k、x,每两个整数之间用一个空格隔开。

【输出】
输出共1行,包含1个整数,表示10k轮后 x号小伙伴所在的位置编号。

【输入样例】
10 3 4 5
【输出样例】
5
【提示】
【数据说明】 

对于 30%的数据,0 < k < 7; 

对于 80%的数据,0 < k < 107; 

对于 100%的数据,1 < n < 1,000,000,0 < m <n,0 ≤ x ≤ n,0 < k < 109
#include <bits/stdc++.h>
#define int long long
using namespace std;
 
int n,m,k,x;
 
inline int gcd(int a,int b){return !b ? a : gcd(b,a%b);}
 
inline int ksm(int a,int b,int mod)
{
	int ans=1;
	a=a%mod;
	while(b)
	{
	  if(b&1) ans=(ans*a)%mod;
	  b>>=1;
	  a=(a*a)%mod;
	}
	return ans;
}
 
signed main()
{
	scanf("%d%d%d%d",&n,&m,&k,&x);
	int lcm=n*m/gcd(n,m);
	cout<<(ksm(10,k,lcm)*m+x)%n;
	return 0;
}
1876:【13NOIP提高组】火柴排队

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 539     通过数: 305 

【题目描述】
涵涵有两盒火柴,每盒装有 nn 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑ni=1(ai−bi)2∑i=1n(ai−bi)2,其中aiai表示第一列火柴中第ii个火柴的高度,bibi表示第二列火柴中第ii个火柴的高度。 

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,99799,999,997 取模的结果。

【输入】
共三行,第一行包含一个整数 nn,表示每盒中火柴的数目。 

第二行有 nn 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。 

第三行有 nn 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

【输出】
输出共一行,包含一个整数,表示最少交换次数对 99,999,99799,999,997 取模的结果。

【输入样例】
4
2 3 1 4
3 2 1 4
【输出样例】
1
【提示】
【输入输出样例说明】 

最小距离是00,最少需要交换11次,比如:交换第11列的前22根火柴或者交换第22列的前22根火柴。

【输入输出样例 2】 

输入:

4
1 3 4 2
1 7 2 4
输出:

2
【输入输出样例说明】 

最小距离是1010,最少需要交换22次,比如:交换第11列的中间22根火柴的位置,再交换第22列中后22根火柴的位置。 

【数据范围】 

对于 10%的数据, 1≤n≤101≤n≤10; 

对于 30%的数据,1≤n≤1001≤n≤100; 

对于 60%的数据,1≤n≤1,0001≤n≤1,000;

对于 100%的数据,1≤n≤100,0001≤n≤100,000,0≤0≤火柴高度≤≤2^{31}-1$
#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#define maxn 100005
#define mod 99999997 
using namespace std;
long long k;
struct data
{
    long long s,id,d;
}a[maxn],b[maxn];
int t[maxn];
bool cmp1(data a,data b)
{
    return a.s<b.s;
}
bool cmp2(data a,data b)
{
    return a.id<b.id;
}
int read(long long &x)
{
    x=0;
    bool ok=0;
    char ch=getchar();
    //if((ch=getchar())==EOF) return 0;

    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    while((ch>='0'&&ch<='9')||ch=='-')
    {
        if(ch=='-') ok=1;
        else x=x*10+ch-'0';
        ch=getchar();
    }
    if(ok) x=-x;
}

long long fuck(int x,int y)
{
    if(x>=y) return 0;
    int m=x+y>>1;
    long long t1=fuck(x,m);
    long long t2=fuck(m+1,y);

    int i=x,j=m+1,k=x;
    long long t3=0;
    while(i<=m&&j<=y)
    {
        if(b[i].d>b[j].d)
        {
            t[k++]=b[j++].d ;
            t3+=(m-i+1)%mod;
        }
        else t[k++]=b[i++].d;
    }
    while(i<=m) t[k++]=b[i++].d;
    while(j<=y) t[k++]=b[j++].d;
    for(int i=x;i<=y;i++) b[i].d=t[i];
    return (t1+t2+t3)%mod;
}

void in()
{
    read(k);
    for(int i=1;i<=k;i++)
    {
        read(a[i].s);
        a[i].id=i;
    }
    for(int i=1;i<=k;i++)
    {
        read(b[i].s);
        b[i].id=i;
    }

    sort(a+1,a+k+1,cmp1);
    sort(b+1,b+k+1,cmp1);
    for(int i=1;i<=k;i++) b[i].d=a[i].id;
    sort(b+1,b+k+1,cmp2);
}

void task()
{
    long long ans=fuck(1,k);
    cout<<ans;
}

int main()
{
   
    in();
    task();
    return 0;
}
1877:【13NOIP提高组】货车运输

时间限制: 5000 ms         内存限制: 131072 KB
提交数: 488     通过数: 255 

【题目描述】
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

【输入】
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。 

接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。 

接下来一行有一个整数 q,表示有 q 辆货车需要运货。 

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

【输出】
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

【输入样例】
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
【输出样例】
3
-1
3

【提示】
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000; 

对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000; 

对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。
#include <bits/stdc++.h>
using namespace std;
 
const int Maxn=10005;
const int Maxm=50005;
int n,m,q,size;
int first[Maxn],f[Maxn][18],d[Maxn][18],depth[Maxn],father[Maxn],vis[Maxn];
struct shu{int to,next,len;};
shu edge[Maxm<<1];
struct kru{int x,y,len;};
kru a[Maxm<<1];
 
inline int get_int()
{
    int x=0,f=1;
    char c;
    for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
    if(c=='-') f=-1,c=getchar();
    for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    return x*f;
}
inline void print(int x)
{
    if(x>9) print(x/10);
    putchar('0'+x%10);
}
 
inline bool comp(const kru &a,const kru &b){return a.len>b.len;}
inline int get(int v){return father[v]==v ? v : father[v]=get(father[v]);}
inline void build(int x,int y,int z)
{
    edge[++size].next=first[x];
    first[x]=size;
    edge[size].to=y,edge[size].len=z;
}
 
inline void dfs(int point,int fa)
{
    for(int i=1;i<=16;i++)
      if(depth[point]>=(1<<i))
      {
      	f[point][i]=f[f[point][i-1]][i-1];
      	d[point][i]=min(d[point][i-1],d[f[point][i-1]][i-1]);
      }
      else break; 
    for(int u=first[point];u;u=edge[u].next)
    {
      int to=edge[u].to;
      if(to==fa) continue;
      f[to][0]=point,d[to][0]=edge[u].len,depth[to]=depth[point]+1;
      dfs(to,point);
    }
}
 
inline int LCA(int x,int y)
{
    if(depth[x] < depth[y]) swap(x,y);
    int len=depth[x]-depth[y];
    for(int i=16;i>=0;i--)
      if(len>=(1<<i)) len-=1<<i,x=f[x][i];
    if(x==y) return x;
    for(int i=16;i>=0;i--)
      if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
} 
 
inline int search(int x,int fa)
{
    int len=depth[x]-depth[fa],minn=1e9;
    for(int i=16;i>=0;i--)
      if(len>=(1<<i))
      	len-=1<<i,minn=min(minn,d[x][i]),x=f[x][i];
    return minn;
}
 
inline int solve(int x,int y)
{
    int fa=LCA(x,y);
    return min(search(x,fa),search(y,fa));
}
 
int main()
{ 
    n=get_int(),m=get_int();
    for(int i=1;i<=n;i++) father[i]=i;
    for(int i=1;i<=m;i++) a[i].x=get_int(),a[i].y=get_int(),a[i].len=get_int();
    sort(a+1,a+m+1,comp);
    for(int i=1;i<=m;i++)
      if(get(a[i].x)!=get(a[i].y))
      	father[get(a[i].x)]=get(a[i].y),build(a[i].x,a[i].y,a[i].len),build(a[i].y,a[i].x,a[i].len);
    q=get_int();
    for(int i=1;i<=n;i++) if(!vis[get(i)]) vis[get(i)]=1,dfs(i,0);
    while(q--)
    {
      int x=get_int(),y=get_int();
      if(get(x)!=get(y)) cout<<"-1\n";
      else print(solve(x,y)),putchar('\n');
    }
    return 0;
}
1878:【13NOIP提高组】积木大赛

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 1156     通过数: 611 

【题目描述】
春春幼儿园举办了一年一度的“积木大赛”。今年比赛的内容是搭建一座宽度为nn的大厦,大厦可以看成由nn块宽度为11的积木组成,第nn块积木的最终高度需要是hihi。 

在搭建开始之前,没有任何积木(可以看成nn块高度为 00 的积木)。接下来每次操作,小朋友们可以选择一段连续区间[LL,RR],然后将第L块到第RR块之间(含第 LL 块和第 RR 块)所有积木的高度分别增加11。 

小MM是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少。但她不是一个勤于动手的孩子,所以想请你帮忙实现这个策略,并求出最少的操作次数。

【输入】
输入包含两行,第一行包含一个整数nn,表示大厦的宽度。 

第二行包含nn个整数,第ii个整数为hihi。

【输出】
仅一行,即建造所需的最少操作数。

【输入样例】
5
2 3 4 1 2
【输出样例】
5
【提示】
【样例解释】 

其中一种可行的最佳方案,依次选择 

[1,5]  [1,3]  [2,3]  [3,3]  [5,5]

【数据范围】 

对于 30%的数据,有1≤n≤101≤n≤10; 

对于 70%的数据,有1≤n≤10001≤n≤1000; 

对于 100%的数据,有1≤n≤1000001≤n≤100000,0≤hi≤100000≤hi≤10000
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
int h[N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]);

    int res = 0;
    for (int i = 1; i <= n; i ++ ){
     if(h[i]-h[i-1]>0)res+=h[i]-h[i-1];
}
    cout << res << endl;

    return 0;
}
 
 

1879:【13NOIP提高组】花匠

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 534     通过数: 306 

【题目描述】
花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。 

具体而言,栋栋的花的高度可以看成一列整数h1,h2,…,hnh1,h2,…,hn。设当一部分花被移走后,

剩下的花的高度依次为g1,g2,...,gmg1,g2,...,gm,则栋栋希望下面两个条件中至少有一个满足: 

条件 AA:对于所有的1≤i≤m21≤i≤m2,有g2i>g2i−1g2i>g2i−1,同时对于所有的1≤i≤m21≤i≤m2,有g2i>g2i+1g2i>g2i+1; 

条件 BB:对于所有的1≤i≤m21≤i≤m2,有g2i<g2i−1g2i<g2i−1,同时对于所有的1≤i≤m21≤i≤m2,有g2i<g2i+1g2i<g2i+1; 

注意上面两个条件在m=1m=1时同时满足,当m>1m>1时最多有一个能满足。 

请问,栋栋最多能将多少株花留在原地。

【输入】
输入的第一行包含一个整数nn,表示开始时花的株数。 

第二行包含nn个整数,依次为h1,h2,…,hnh1,h2,…,hn,表示每株花的高度。

【输出】
输出一行,包含一个整数mm,表示最多能留在原地的花的株数。

【输入样例】
5
5 3 2 1 2
【输出样例】
3
【提示】
【输入输出样例说明】 

有多种方法可以正好保留 33 株花,例如,留下第 11、44、55 株,高度分别为 55、11、22,满足条件 BB。 

【数据范围】 

对于 20%的数据,n≤10n≤10; 

对于 30%的数据,n≤25n≤25; 

对于 70%的数据,n≤1000,0≤hi≤1000n≤1000,0≤hi≤1000;

对于100%的数据,1≤n≤100,000,0≤hi≤1,000,0001≤n≤100,000,0≤hi≤1,000,000,所有的hihi随机生成,所有随机数服从某区间内的均匀分布。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
const int maxn=100007;
const int zuida=1000000;
struct node{
    int fda,gda;
}t[zuida];
struct nod{
    int a,b;
}b[maxn];
using namespace std;
int i,j,k,l,n,m,ans,tot;
int a[maxn],f[maxn],g[maxn];
void insert(int x,int l,int r,int y,int z,int p){
    if(l==r){
        if(p==1)t[x].fda=max(t[x].fda,z);
        else t[x].gda=max(t[x].gda,z);    
    }
    else{
        int mid=(l+r)/2;
        if(y<=mid)insert(x*2,l,mid,y,z,p);else insert(x*2+1,mid+1,r,y,z,p);
        if(p==1)t[x].fda=max(t[x*2].fda,t[x*2+1].fda);
        else t[x].gda=max(t[x*2].gda,t[x*2+1].gda); 
    }
}
int find(int x,int l,int r,int y,int z,int p){
    if(y>z)return 0;
    if(l==y&&r==z){
        if(p==1)return t[x].fda;
        else return t[x].gda;
    }
    else{
        int mid=(l+r)/2;
        if(z<=mid)return find(x*2,l,mid,y,z,p);else if (y>mid)return find(x*2+1,mid+1,r,y,z,p);
        else{
            return max(find(x*2,l,mid,y,mid,p),find(x*2+1,mid+1,r,mid+1,z,p));
        }
    }
}
bool cmp(nod x,nod y){
    return x.a<y.a;
}
int main(){
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d",&b[i].a);b[i].b=i;
    }
    sort(b+1,b+1+n,cmp);
    a[b[1].b]=1;tot=1;
    fo(i,2,n){
        if(b[i-1].a==b[i].a)a[b[i].b]=tot;
        else a[b[i].b]=++tot;
    }
    f[1]=g[1]=1;
    insert(1,1,tot,a[1],f[1],1);
    insert(1,1,tot,a[1],g[1],2);
    fo(i,2,n){
        f[i]=find(1,1,tot,1,a[i]-1,2)+1;
        g[i]=find(1,1,tot,a[i]+1,tot,1)+1;
        insert(1,1,tot,a[i],f[i],1);
        insert(1,1,tot,a[i],g[i],2);
        ans=max(ans,f[i]);
        ans=max(ans,g[i]);
    }
    printf("%d\n",ans);
}
 
 

1880:【13NOIP提高组】华容道

时间限制: 1000 ms         内存限制: 131072 KB
提交数: 297     通过数: 123 

【题目描述】
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。 

小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的: 

1.在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的; 

2.有些棋子是固定的,有些棋子则是可以移动的; 

3.任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。 

给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EXi 行第 EYi 列,指定的可移动棋子的初始位置为第 SXi 行第 SYi列,目标位置为第 TXi 行第 TYi 列。 

假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

【输入】
第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q; 

接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。 

接下来的 q 行,每行包含 6 个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

【输出】
输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。

【输入样例】
3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2
【输出样例】
2
-1

【提示】
【输入输出样例说明】 

棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。 

1. 第一次游戏,空白格子的初始位置是(3,2)(图中空白所示),游戏的目标是将初始位置在(1,2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2,  2)(图中红色的格子)上。 

移动过程如下:



2. 第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2,2)上的棋子(图中绿色圆圈所示)移动到目标位置(3, 2)上。



要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。 

【数据范围】 

对于 30%的数据,1 ≤ n, m ≤ 10,q = 1; 

对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10; 

对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 35, INF = 0x3f3f3f3f;
const int MOVE[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};

bool ori[N][N];
int n, m, qq;
struct point{
	int x, y;
	inline void input() {
		scanf("%d%d", &x, &y);
	}
	inline bool operator == (const point &rhs) const {
		return x == rhs.x and y == rhs.y;
	}
	inline point move(int k) {
		point now = (point){x + MOVE[k][0], y + MOVE[k][1]};
		return now;
	}
	inline bool psb() {
		return ori[x][y] && 1 <= x && x <= n && 1 <= y && y <= m;
	}
};

typedef pair<point, int> pwd;
queue<pwd> pq;
queue<point> q;
int distB[N][N], dist[N][N][4][4];
bool visit[N][N];
#define mp make_pair
inline void initBlank(point center) {
	for (int k = 0; k < 4; k++) {
		point tmp = center.move(k);
		if (tmp.psb()) pq.push(mp(tmp, k));
	}
	while (! pq.empty()) {
		point st = pq.front().first; 
		int k1 = pq.front().second; pq.pop();
		while (! q.empty()) q.pop();
		memset(visit, 0, sizeof(visit));
		distB[st.x][st.y] = 0; q.push(st); visit[st.x][st.y] = true;
		while (! q.empty()) {
			point dir = q.front(); q.pop();
			for (int k = 0; k < 4; k++) {
				point tmp = dir.move(k);
				if (! tmp.psb() or tmp == center) continue;
				if (! visit[tmp.x][tmp.y]) {
					distB[tmp.x][tmp.y] = distB[dir.x][dir.y] + 1;
					q.push(tmp); visit[tmp.x][tmp.y] = true;
				}
			}
		}
		for (int k2 = 0; k2 < 4; k2++) if (k1 != k2) {
			point tmp = center.move(k2);
			if (tmp.psb() && visit[tmp.x][tmp.y]) {
				dist[center.x][center.y][k1][k2] = distB[tmp.x][tmp.y];
			}
		}
	}
}

inline void preWork() {
	memset(dist, 0x3f, sizeof(dist));
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) if (ori[i][j]) {
			point tmp = (point){i, j};
			initBlank(tmp);
		}
}

point blank, src, tar;

inline void preSpfa() {
	memset(distB, 0x3f, sizeof(distB));
	distB[blank.x][blank.y] = 0;
	q.push(blank);
	while (! q.empty()) {
		point dir = q.front(); q.pop();
		for (int k = 0; k < 4; k++) {
			point tmp = dir.move(k);
			if (tmp.psb() && ! (tmp == src))
				if (distB[tmp.x][tmp.y] == INF) {
					distB[tmp.x][tmp.y] = distB[dir.x][dir.y] + 1;
					q.push(tmp);
				}
		}
	}
}

int dis[N][N][4];
bool inq[N][N][4];
#define inQ(rhs) inq[rhs.first.x][rhs.first.y][rhs.second]
#define diS(rhs) dis[rhs.first.x][rhs.first.y][rhs.second]
inline int spfa() {
	if (src == tar) return 0;
	preSpfa();
	memset(dis, 0x3f, sizeof(dis));
	for (int k = 0; k < 4; k++) {
		point dir = src.move(k);
		if (dir.psb() && distB[dir.x][dir.y] != INF) {
			dis[src.x][src.y][k] = distB[dir.x][dir.y];
			pwd now = mp(src, k);
			pq.push(now); inQ(now) = true;
		}
	}
	
	while (! pq.empty()) {
		pwd dir = pq.front(); pq.pop();
		inQ(dir) = false;
		point tmp1 = dir.first.move(dir.second);
		pwd tmp2 = mp(tmp1, dir.second^1);
		if (diS(tmp2) > diS(dir) + 1) {
			diS(tmp2) = diS(dir) + 1;
			if (! inQ(tmp2)) {
				pq.push(tmp2); inQ(tmp2) = true;
			}
		}

		for (int i = 0; i < 4; i++) if (i != dir.second) {
			tmp1 = dir.first.move(i);
			int &l = dist[dir.first.x][dir.first.y][dir.second][i];
			if (! tmp1.psb() || l == INF) continue;
			tmp2 = mp(dir.first, i);
			if (diS(tmp2) > diS(dir) + l) {
				diS(tmp2) = diS(dir) + l;
				if (! inQ(tmp2)) {
					pq.push(tmp2); inQ(tmp2) = true;
				}
			}
		}
	}
	int ans = INF;
	for (int k = 0; k < 4; k++)
		ans = min(ans, dis[tar.x][tar.y][k]);
	return ans == INF ? -1 : ans;
}

int main() {
	cin >> n >> m >> qq;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			int tmp; scanf("%d", &tmp);
			ori[i][j] = tmp;
		}
	preWork();
	while (qq--) {
		blank.input(); src.input(); tar.input();
		printf("%d\n", spfa());
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值