NOIP提高组1900~1909以及1986集合答案

1900:【17NOIP提高组】时间复杂度

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 670     通过数: 287 

【题目描述】
给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序,于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。 A++A++ 语言的循环结构如下:

F i x y
    循环体
E
然后判断 ii 和 yy 的大小关系,若 ii 小于等于 yy 则进入循环,否则不进入。每次循环结束后 ii 都会被修改成 i+1i+1,一旦 ii 大于 yy 终止循环。 xx 和 yy 可以是正整数(xx 和 yy 的大小关系不定)或变量 nn。nn 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100100。 EE表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。

注:本题中为了书写方便,在描述复杂度时,使用大写英文字母 OO 表示通常意义下 ΘΘ 的概念。

【输入】
第一行一个正整数 tt,表示有 t(t≤10)t(t≤10) 个程序需要计算时间复杂度。

每个程序我们只需抽取其中 FixyFixy和EE即可计算时间复杂度。注意:循环结构允许嵌套。

接下来每个程序的第一行包含一个正整数 LL 和一个字符串,LL 代表程序行数,字符串表示这个程序的复杂度,O(1)O(1)表示常数复杂度,O(nO(n^w)w) 表示复杂度为 nwnw ,其中 ww 是一个小于 100100 的正整数(输入中不包含引号),输入保证复杂度只有 O(1)O(1) 和 O(nO(n^w)w) 两种类型。

接下来 LL 行代表程序中循环结构中的 FixyFixy 或者 EE。 程序行若以 FF 开头,表示进入一个循环,之后有空格分离的三个字符(串)ixyixy,其中 ii 是一个小写字母(保证不为 nn ),表示新建的变量名,xx 和 yy 可能是正整数或 nn ,已知若为正整数则一定小于 100100。 程序行若以 EE开头,则表示循环体结束。

【输出】
输出共 tt 行,对应输入的 tt 个程序,每行输出YesYes或NoNo或者ERRERR,若程序实际复杂度与输入给出的复杂度一致则输出 YesYes,不一致则输出NoNo,若程序有语法错误(其中语法错误只有: ①FF 和 EE 不匹配 ②新建的变量与已经存在但未被销毁的变量重复两种情况),则输出ERRERR。

注意:即使在程序不会执行的循环体中出现了语法错误也会编译错误,要输出ERRERR。

【输入样例】
8 
2 O(1) 
F i 1 1 
E 
2 O(n^1) 
F x 1 n 
E
1 O(1) 
F x 1 n 
4 O(n^2) 
F x 5 n 
F y 10 n 
E 
E 
4 O(n^2) 
F x 9 n 
E
F y 2 n 
E 
4 O(n^1) 
F x 9 n 
F y n 4 
E 
E 
4 O(1) 
F y n 4 
F x 9 n 
E 
E 
4 O(n^2)
F x 1 n 
F x 1 10 
E 
E
【输出样例】
Yes 
Yes 
ERR 
Yes 
No 
Yes 
Yes 
ERR
【提示】
【样例说明】

第一个程序 ii 从11 到 11 是常数复杂度。

第二个程序 xx 从 11 到 nn 是 nn 的一次方的复杂度。

第三个程序有一个 FF 开启循环却没有E结束,语法错误。

第四个程序二重循环,nn 的平方的复杂度。

第五个程序两个一重循环,nn 的一次方的复杂度。

第六个程序第一重循环正常,但第二重循环开始即终止(因为 nn 远大于 100100,100100 大于 44)。

第七个程序第一重循环无法进入,故为常数复杂度。

第八个程序第二重循环中的变量 xx 与第一重循环中的变量重复,出现语法错误②,输出 ERRERR。

【数据范围】

对于 30% 的数据:不存在语法错误,数据保证小明给出的每个程序的前 L/2L/2 行一定为以 FF 开头的语句,第 L/2+1L/2+1 行至第 LL 行一定为以 EE 开头的语句,L≤10L≤10,若 xx,yy 均为整数,xx 一定小于 yy,且只有 yy 有可能为 nn。

对于 50% 的数据:不存在语法错误,L≤100L≤100,且若 xx,yy 均为整数,xx 一定小于 yy,且只有 yy 有可能为 nn。

对于 70% 的数据:不存在语法错误,L≤100L≤100。

对于 100% 的数据:t≤10,L≤100t≤10,L≤100。

样例数据下载
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<string>
using namespace std;
map<string,int>M;
map<int,string>used;
int t,l,tt,qw,t1,t2,flag,pd1,pd2,qww,l1,l2,ffl;
char ss[12],q1[20],q2[20],q3[20],q4[20];
int main()
{
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		scanf("%d",&l);scanf("%s",ss);
		flag=0,tt=0,qw=0,pd1=0,pd2=0,qww=0,ffl=0;M.clear();used.clear();
		if(ss[2]=='1')pd1=1;
		else 
		{
			pd2=ss[4]-'0';
			if(ss[5]<='9'&&ss[5]>='0')
			{
				pd2=pd2*10+ss[5]-'0';
				if(ss[6]<='9'&&ss[6]>='0')pd2=pd2*10+ss[6]-'0';
			}
		}
		for(int j=1;j<=l;j++)
		{
			scanf("%s",q1);
			if(q1[0]=='E')
			{
				if(!tt)
				{
					flag=1;
				}
				M[used[tt]]=0;
				if(tt==ffl)ffl=0;
				tt--;
				qww=max(qww,qw);
				if(qw)qw--;
				//if(!tt)M.clear();
			}else
			{
				tt++;
				scanf("%s",q2);scanf("%s",q3);scanf("%s",q4);
				if(M[q2])
				{
					flag=1;
				}
				used[tt]=q2;
				M[used[tt]]=1;l1=strlen(q3);l2=strlen(q4);t1=0,t2=0;
				for(int qq=0;qq<l1;qq++)t1=t1*10+q3[qq]-'0';
				for(int qq=0;qq<l2;qq++)t2=t2*10+q4[qq]-'0';
				if(((t1>t2)&&q4[0]!='n')||(q3[0]=='n'&&q4[0]!='n'))
				{
					if(!ffl)ffl=tt;
				}else
				{
					if((q4[0]=='n')&&(!ffl)&&(q3[0]!='n'))qw++;
				}
			}
		}
		if(flag==1)
		{
			printf("ERR\n");continue;
		}
		if(tt)
		{
			printf("ERR\n");continue;
		}
		if(pd1&&!qww)
		{
			printf("Yes\n");continue;
		}
		if(qww==pd2)
		{
			printf("Yes\n");continue;
		}
		printf("No\n");
	}
	return 0;
}
 
 

1901:【17NOIP提高组】逛公园

时间限制: 3000 ms         内存限制: 524288 KB
提交数: 542     通过数: 183 

【题目描述】
策策同学特别喜欢逛公园。 公园可以看成一张 NN 个点 MM 条边构成的有向图,且没有自环和重边。其中 11 号点是公园的入口, NN 号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从 11 号点进去,从 NN 号点出来。

策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果 11 号点到 NN 号点的最短路长为 dd,那么策策只会喜欢长度不超过 d+Kd+K 的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?

为避免输出过大,答案对 PP 取模。

如果有无穷多条合法的路线,请输出 −1−1。

【输入】
第一行包含一个整数 TT, 代表数据组数。

接下来 TT 组数据,对于每组数据:

第一行包含四个整数 N,M,K,PN,M,K,P, 每两个整数之间用一个空格隔开。

接下来 MM 行,每行三个整数 ai,bi,ciai,bi,ci ,代表编号为 ai,biai,bi 的点之间有一条权值为 cici 的有向边,每两个整数之间用一个空格隔开。

【输出】
输出包含 TT 行,每行一个整数代表答案。

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

对于第一组数据,最短路为 33。

1−5,1−2−4−5,1−2−3−51−5,1−2−4−5,1−2−3−5 为 33 条合法路径。

对于不同测试点,我们约定各种参数的规模不会超过如下

测试点编号 T N M K 是否有 0 边 
1 5 5 10 0 否 
2 5 1000 2000 0 否 
3 5 1000 2000 50 否 
4 5 1000 2000 50 否 
5 5 1000 2000 50 否 
6 5 1000 2000 50 是 
7 5 100000 200000 0 否 
8 3 100000 200000 50 否 
9 3 100000 200000 50 是 
10 3 100000 200000 50 是 

对于 100% 的数据:1≤P≤109,1≤ai,bi≤N,0≤ci≤10001≤P≤109,1≤ai,bi≤N,0≤ci≤1000。

数据保证:至少存在一条合法的路线。

样例数据下载
#include <bits/stdc++.h>
using namespace std;
 
const int Maxn=100010;
const int Maxm=200010;
int t,n,m,size,k,mod,ans,minn,flag;
int dis[Maxn],First[Maxn],first[Maxn],f[Maxn][55],vis[Maxn][55];
struct shu{int to,next,len;};
shu Edge[Maxm],edge[Maxm];
priority_queue<pair<int,int> >q;
 
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 clean()
{
	size=ans=flag=0;
	memset(dis,0x3f3f3f,sizeof(dis));
	memset(First,0,sizeof(First));
	memset(first,0,sizeof(first));
	memset(f,-1,sizeof(f));
	memset(vis,0,sizeof(vis));
}
 
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;
	edge[size].next=first[y];
	first[y]=size;
	edge[size].to=x,edge[size].len=z;
}
 
inline void dijkstra()
{
	q.push(make_pair(0,1)),dis[1]=0;
	while(!q.empty())
	{
	  int point=q.top().second;
	  q.pop();
	  for(int u=First[point];u;u=Edge[u].next)
	  {
	  	int to=Edge[u].to;
	  	if(dis[to] > dis[point] + Edge[u].len)
	  	{
	  	  dis[to]=dis[point] + Edge[u].len;
	  	  q.push(make_pair(-dis[to],to));
	  	}
	  }
	}
}
 
inline int dfs(int point,int k)
{
	if(~f[point][k]) return f[point][k];
	f[point][k]=0,vis[point][k]=1;
	for(register int u=first[point];u;u=edge[u].next)
	{
	  int to=edge[u].to,num=dis[point]-dis[to]+k-edge[u].len;
	  if(num<0) continue;
	  if(vis[to][num]) flag=1;
	  f[point][k]=(f[point][k]+dfs(to,num))%mod;
	}
	vis[point][k]=0;
	return f[point][k];
}
 
int main()
{
	t=get_int();
	while(t--)
	{
	  clean();
	  n=get_int(),m=get_int(),k=get_int(),mod=get_int();
	  for(int i=1;i<=m;i++)
	  {
	  	int x=get_int(),y=get_int(),z=get_int();
	  	build(x,y,z);
	  }
	  dijkstra();
	  if(!dis[n]) {cout<<"-1\n";continue;}
	  f[1][0]=1;
	  for(int i=0;i<=k;i++) ans=(ans+dfs(n,i))%mod;
	  if(!flag) cout<<ans<<"\n";
	  else cout<<"-1\n";
	}
	return 0;
}
1902:【17NOIP提高组】奶酪

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 1032     通过数: 337 

【题目描述】
现有一块大奶酪,它的高度为 hh,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z=0z=0,奶酪的上表面为 z=hz=h。

现在, 奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别 地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。

位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?

空间内两点 P1(x1,y1,z1)、P2(x2,y2,z2)P1(x1,y1,z1)、P2(x2,y2,z2) 的距离公式如下:

dist(P1,P2)=(x1−x2)2+(y1−y2)2+(z1−z2)2dist(P1,P2)=(x1−x2)2+(y1−y2)2+(z1−z2)2

【输入】
每个输入包含多组数据。

输入的第一行,包含一个正整数 TT,代表该输入文件中所含的数据组数。

接下来是 TT 组数据,每组数据的格式如下:

第一行包含三个正整数 n,hn,h 和 rr,两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。

接下来的 nn 行,每行包含三个整数 x,y,zx,y,z,两个数之间以一个空格分开,表示空洞球心坐标为 (x,y,zx,y,z)。

【输出】
输出包含 TT 行,分别对应 TT 组数据的答案,如果在第 ii 组数据中, Jerry 能从下表面跑到上表面,则输出“YesYes”,如果不能,则输出“NoNo”(均不包含引号)。

【输入样例】
3
2 4 1
0 0 1
0 0 3
2 5 1
0 0 1
0 0 4
2 5 2
0 0 2
2 0 4
【输出样例】
Yes
No
Yes
【提示】
样例说明:



对于 20%的数据,n=1,1≤h,r≤10,000n=1,1≤h,r≤10,000,坐标的绝对值不超过 10,00010,000。

对于 40%的数据,1≤n≤8,1≤h,r≤10,0001≤n≤8,1≤h,r≤10,000,坐标的绝对值不超过 10,00010,000。

对于 80%的数据,1≤n≤1,000,1≤h,r≤10,0001≤n≤1,000,1≤h,r≤10,000,坐标的绝对值不超过 10,00010,000。

对于 100%的数据,1≤n≤1,000,1≤h,r≤1,000,000,000,T≤201≤n≤1,000,1≤h,r≤1,000,000,000,T≤20,坐标的绝对值不超过 1,000,000,0001,000,000,000。

样例数据下载
#include <bits/stdc++.h>
using namespace std;
const int Max=1010;
int t,n,h,r,flag,father[Max];
struct shu{long long x,y,z;};
shu a[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 bool comp(const shu &a,const shu &b){return a.z<b.z;}
inline double calc(int i,int j){return sqrt(double(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z));}
inline int getfather(int v){return father[v]==v ? v : father[v]=getfather(father[v]);}
int main()
{
   t=get_int();
   while(t--)
   {
   	 memset(a,0,sizeof(a));
   	 n=get_int(),h=get_int(),r=get_int();
   	 for(int i=1;i<=n;i++) a[i].x=get_int(),a[i].y=get_int(),a[i].z=get_int();
   	 sort(a+1,a+n+1,comp);
   	 for(int i=1;i<=n;i++) father[i]=i;
   	 if(a[n].z+r<h || a[1].z>r) {cout<<"No\n";continue;}
     for(int i=1;i<=n;i++)
       for(int j=i+1;j<=n;j++)
       	 if(calc(i,j)<=2*r)
       	 {
       	   int fax=getfather(i),fay=getfather(j);
       	   if(fax!=fay) father[fay]=fax;
       	 }
     flag=0;
     for(int i=n;i>=1;i--)
     {
       if(flag) break;
       if(a[i].z+r<h || flag) break;
       for(int j=1;j<=n;j++)
	   {
	   	 if(a[j].z>r) break;
	     if(getfather(j)==getfather(i)&&a[j].z+r>=0) {flag=1;break;}
	   }
     }
     cout<<(flag ? "Yes\n" : "No\n");
   }
 
   return 0;
}
1903:【17NOIP提高组】宝藏

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 415     通过数: 223 

【题目描述】
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nn 个深埋在地下的宝藏屋,也给出了这 nn 个宝藏屋之间可供开发的 mm 条道路和它们的长度。

小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路则相对容易很多。

小明的决心感动了考古挖掘的赞助商, 赞助商决定免费赞助他打通一条从地面到某个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

在此基础上, 小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏屋之间的道路无需再开发。

新开发一条道路的代价是:

这条道路的长度 × 从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋)。

请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值。

【输入】
第一行两个用空格分离的正整数 nn 和 mm,代表宝藏屋的个数和道路数。

接下来 mm 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏屋的编号(编号为 11∼nn),和这条道路的长度 vv。

【输出】
输出共一行,一个正整数,表示最小的总代价。

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



小明选定让赞助商打通了11号宝藏屋。小明开发了道路1→21→2,挖掘了22号宝藏。开发了道路1→41→4,挖掘了44号宝藏。还开发了道路4→34→3,挖掘了33号宝藏。工程总代价为:1×1+1×1+1×2=41×1+1×1+1×2=4
(1→2)(1→4)(4→3)(1→2)(1→4)(4→3)    

样例输入2

4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 2
样例输出2

5
样例说明2



小明选定让赞助商打通了11号宝藏屋。小明开发了道路1→21→2,挖掘了22号宝藏。开发了道路1→31→3,挖掘了33号宝藏。还开发了道路1→41→4,挖掘了44号宝藏。工程总代价为:1×1+3×1+1×1=51×1+3×1+1×1=5
(1→2)(1→3)(1→4)(1→2)(1→3)(1→4) 

数据范围与提示

对于 20% 的数据:保证输入是一棵树, 1≤n≤8,v≤50001≤n≤8,v≤5000 且所有的 vv 都相等。

对于 40% 的数据:1≤n≤8,0≤m≤1000,v≤50001≤n≤8,0≤m≤1000,v≤5000 且所有的 vv 都相等。

对于 70% 的数据:1≤n≤8,0≤m≤1000,v≤50001≤n≤8,0≤m≤1000,v≤5000。

对于 100% 的数据:1≤n≤12,0≤m≤1000,v≤5000001≤n≤12,0≤m≤1000,v≤500000。

样例数据下载
#include <bits/stdc++.h>
using namespace std;
 
const int Max=5000;
const int inf=1e9;
int n,m,ans=1e9,tot;
int dis[20][20],f[20][Max];
int p[Max],num[Max],pos[Max],sum[Max],condition[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 int mn(int x,int y){return x < y ? x : y;}
 
int main()
{
	n=get_int(),m=get_int();
	memset(f,0x3f,sizeof(f));
	for(int i=0;i<n;i++)for(int j=0;j<n;j++) dis[i][j]=10000000;
	for(int i=1;i<=m;i++)
	{
	  int x=get_int()-1,y=get_int()-1,z=get_int();
	  dis[x][y]=dis[y][x]=mn(z,dis[x][y]);
	}
	for(int i=0;i<n;i++) pos[1<<i]=i,f[0][1<<i]=0;
	for(int i=0;i<n;i++)
	  for(int j=0;j<(1<<n);j++)
	  {
	  	tot=0;
	  	for(int to=0;to<n;to++)
	    {
	      if((1<<to)&j) continue;
	      p[tot]=1<<to;
	      num[tot]=10000000;
	      for(int k=j;k;k-=k&(-k))
	      {
	      	int from=pos[k&(-k)];
	      	num[tot]=mn(num[tot],dis[from][to]*(i+1));
	      }
	      tot++;
	    }
	    for(int k=1;k<(1<<tot);k++)
	    {
	      condition[k]=condition[k-(k&(-k))] | p[pos[k&(-k)]];
	      sum[k]=sum[k-(k&(-k))] + num[pos[k&(-k)]];
	      f[i+1][j|condition[k]]=mn(f[i+1][j|condition[k]],f[i][j]+sum[k]);
	    }
	  }
	  for(int i=0;i<=n;i++) ans=mn(ans,f[i][(1<<n)-1]);
	  cout<<ans<<"\n";
	  return 0;
}
1904:【17NOIP提高组】列队

时间限制: 2000 ms         内存限制: 524288 KB
提交数: 261     通过数: 99 

【题目描述】
Sylvia 是一个热爱学习的女孩子。

前段时间, Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。 Sylvia所在的方阵中有 n×mn×m名学生,方阵的行数为 nn,列数为 mm。

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从 11 到 n×mn×m 编上了号码(参见后面的样例)。即:初始时,第 ii 行第 jj 列的学生的编号是 (i−1)×m+j(i−1)×m+j。

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了 qq 件这样的离队事件。每一次离队事件可以用数对 (x,y)(1≤x≤n,1≤y≤m)(x,y)(1≤x≤n,1≤y≤m) 描述, 表示第 xx 行第 yy 列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令:

1、向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第 xx 行第 mm 列。

2、向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第 nn 行第 mm 列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 nn 行第 mm 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

【输入】
输入共 q+1q+1 行。

第 11 行包含 33 个用空格分隔的正整数 n,m,qn,m,q,表示方阵大小是 nn 行 mm 列,一共发生了 qq 次事件。

接下来 qq 行按照事件发生顺序描述了 qq 件事件。每一行是两个整数 x,yx,y, 用一个空格分隔, 表示这个离队事件中离队的学生当时排在第 xx 行第 yy 列。

【输出】
按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学生的编号。

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

[1324]⇒[324]⇒[234]⇒[234]⇒[2341][1234]⇒[234]⇒[234]⇒[243]⇒[2431]
[2341]⇒[234]⇒[234]⇒[234]⇒[2341][2431]⇒[243]⇒[243]⇒[243]⇒[2431]
[2341]⇒[231]⇒[231]⇒[231]⇒[2314][2431]⇒[231]⇒[231]⇒[213]⇒[2134]
列队的过程如上图所示,每一行描述了一个事件。

在第一个事件中,编号为 11 的同学离队,这时空位在第一行第一列。接着所有同学向左标齐,这时编号为 22 的同学向左移动一步,空位移动到第一行第二列。然后所有同学向上标齐,这时编号为 44 的同学向上一步,这时空位移动到第二行第二列。最后编号为 11 的同学返回填补到空位中。

数据范围与提示

测试点编号 nn mm qq 其它约定 
1,2,3,4,5,61,2,3,4,5,6 ≤1000≤1000 ≤1000≤1000 ≤500≤500 无 
7,8,9,107,8,9,10 ≤1000≤1000 ≤1000≤1000 ≤500≤500 
11,1211,12 =1=1 ≤105≤105 ≤105≤105 所有事件x=1x=1 
13,1413,14 ≤3×105≤3×105 ≤3×105≤3×105 
15,1615,16 ≤3×105≤3×105 
17,1817,18 ≤105≤105 ≤105≤105 ≤105≤105 无 
19,2019,20 ≤3×105≤3×105 ≤3×105≤3×105 ≤3×105≤3×105 

数据保证每一个事件满足1≤x≤n,1≤y≤m1≤x≤n,1≤y≤m
样例数据下载
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int Max=300005;
int n,m,q,sum,tot;
int tree[Max*20],lc[Max*20],rc[Max*20],rt[Max];
vector<ll>num[Max];
inline int get_int()
{
	int x=0;char c;
	for(c=getchar();!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
	return x;
}
inline void print(ll x)
{
	if(x>9) print(x/10);
	putchar('0'+x%10);
}
 
inline void add(int &now,int l,int r,int pos)
{
	if(!now) now=++tot;tree[now]++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(pos<=mid) add(lc[now],l,mid,pos);
	else add(rc[now],mid+1,r,pos);
}
inline int Q(int root,int l,int r,int k)
{
	if(l==r) return l;
	int mid=(l+r)>>1,sum=(mid-l+1)-tree[lc[root]];
	if(sum>=k) Q(lc[root],l,mid,k);
	else Q(rc[root],mid+1,r,k-sum);
}
inline ll Qr(int x,ll v)
{
	int pos=Q(rt[n+1],1,sum,x);add(rt[n+1],1,sum,pos);
	ll ans=pos<=n?(ll)m*pos:num[n+1][pos-n-1];
	return num[n+1].push_back(v?v:ans),ans;
}
inline ll Ql(int x,int y)
{
	int pos=Q(rt[x],1,sum,y);add(rt[x],1,sum,pos);
	ll ans=pos<m?(ll)(x-1)*m+pos:num[x][pos-m];
	return num[x].push_back(Qr(x,ans)),ans;
}
 
int main()
{
  	n=get_int(),m=get_int(),q=get_int(),sum=max(n,m)+q;
	while(q--)
	{
	  int x=get_int(),y=get_int();
	  if(y==m) print(Qr(x,0)),putchar('\n');
	  else print(Ql(x,y)),putchar('\n');
	}
	return 0;
}
1905:【18NOIP提高组】货币系统

时间限制: 1000 ms         内存限制: 524288 KB
提交数: 1500     通过数: 679 

【题目描述】
在网友的国度中共有 nn 种不同面额的货币,第 ii 种货币的面额为 a[i]a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 nn、面额数组为 a[1..n]a[1..n]的货币系统记作 (n,a)(n,a)。

在一个完善的货币系统中,每一个非负整数的金额 xx 都应该可以被表示出,即对每一个非负整数 xx,都存在 nn 个非负整数 t[i]t[i] 满足 a[i]×t[i]a[i]×t[i] 的和为 xx。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 xx 不能被该货币系统表示出。例如在货币系统 n=3n=3, a=[2,5,9]a=[2,5,9] 中,金额 1,31,3 就无法被表示出来。

两个货币系统 (n,an,a) 和 (m,bm,b) 是等价的,当且仅当对于任意非负整数 xx,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,bm,b),满足(m,bm,b) 与原来的货币系统 (n,an,a) 等价,且 mm 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 mm。

【输入】
输入的第一行包含一个整数 TT,表示数据的组数。接下来按照如下格式分别给出 TT 组数据。

每组数据的第一行包含一个正整数 nn。接下来一行包含 nn 个由空格隔开的正整数a[i]a[i]。

【输出】
输出文件共有 TT 行,对于每组数据,输出一行一个正整数,表示所有与 (n,an,a) 等价的货币系统 (m,bm,b) 中,最小的 mm。

【输入样例】
2
4
3 19 10 6
5
11 29 13 19 17
【输出样例】
2
5
【提示】
【输入输出样例1说明】

在第一组数据中,货币系统 (2,[3,10]2,[3,10]) 和给出的货币系统 (n,an,a) 等价,并可以验证不存在 m<2m<2 的等价的货币系统,因此答案为 22。

在第二组数据中,可以验证不存在 m<nm<n 的等价的货币系统,因此答案为 55。

【数据规模与约定】

测试点 nn aiai 测试点 nn aiai 
11 =2=2 ≤1000≤1000 1111 ≤13≤13 ≤16≤16 
22 1212 
33 1313 
44 =3=3 1414 ≤25≤25 ≤40≤40 
55 1515 
66 1616 
77 =4=4 1717 ≤100≤100 ≤25000≤25000 
88 1818 
99 =5=5 1919 
1010 2020 

对于100%的数据,满足1≤T≤20,n,a[i]≥11≤T≤20,n,a[i]≥1。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 105
#define M 25005
using namespace std;
int a[N],f[M];
int main()
{

	int n,i,j,T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		int Max=0,ans=0;
		for(i=1;i<=n;++i)
		{
			scanf("%d",&a[i]);
			Max=max(Max,a[i]);
		}
		memset(f,0,sizeof(f));
		f[0]=1;
		for(i=1;i<=n;++i)
		    for(j=a[i];j<=Max;++j)
		        f[j]+=f[j-a[i]];
		for(i=1;i<=n;++i)
		    if(f[a[i]]==1)
		        ans++;
		printf("%d\n",ans);
	}

	return 0;
}
1906:【18NOIP提高组】赛道修建

时间限制: 1000 ms         内存限制: 524288 KB
提交数: 532     通过数: 220 

【题目描述】
C 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 mm 条赛道。

C 城一共有nn 个路口,这些路口编号为 1,2,…,n1,2,…,n,有n−1n−1 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 ii 条道路连接的两个路口编号为aiai和bibi,该道路的长度为lili。借助这 n−1n−1 条道路,从任何一个路口出发都能到达其他所有的路口。

一条赛道是一组互不相同的道路e1,e2,…,eke1,e2,…,ek,满足可以从某个路口出发,依次经过道路e1,e2,…,eke1,e2,…,ek(每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。

目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的m条赛道中长度最小的赛道长度最大(即m条赛道中最短赛道的长度尽可能大)。

【输入】
输入第一行包含两个由空格分隔的正整数n,mn,m,分别表示路口数及需要修建的赛道数。

接下来n−1n−1 行,第i 行包含三个正整数ai,bi,liai,bi,li,表示第ii 条适合于修建赛道的道路连接的两个路口编号及道路长度。保证任意两个路口均可通过这n−1n−1 条道路相互到达。每行中相邻两数之间均由一个空格分隔。

【输出】
输出共一行,包含一个整数,表示长度最小的赛道长度的最大值。

【输入样例】
7 1
1 2 10
1 3 5
2 4 9
2 5 8
3 6 6
3 7 7
【输出样例】
31
【提示】
【输入输出样例1说明】

所有路口及适合于修建赛道的道路如下图所示:



道路旁括号内的数字表示道路的编号,非括号内的数字表示道路长度。

需要修建 11 条赛道。可以修建经过第 3,1,2,63,1,2,6 条道路的赛道(从路口 44 到路口 77),则该赛道的长度为 9+10+5+7=319+10+5+7=31,为所有方案中的最大值。

【样例输入2】

9 3
1 2 6
2 3 3
3 4 5
4 5 10
6 2 4
7 2 9
8 4 7
9 4 4
【样例输出2】

15
【输入输出样例2说明】

所有路口及适合于修建赛道的道路如下图所示:



需要修建 33 条赛道。可以修建如下 33 条赛道:

1.经过第 1,61,6 条道路的赛道(从路口 11 到路口 77),长度为 6+9=156+9=15;

2.经过第 5,2,3,85,2,3,8 条道路的赛道(从路口 66 到路口 99),长度为 4+3+5+4=164+3+5+4=16;

3.经过第 7,47,4 条道路的赛道(从路口 88 到路口 55),长度为 7+10=177+10=17。长度最小的赛道长度为 1515,为所有方案中的最大值。

【输入输出样例3】

样例数据3

【数据规模与约定】

所有测试数据的范围和特点如下表所示:

测试点编号 nn mm ai=1ai=1 bi=ai+1bi=ai+1 分支不超过33 
11 ≤5≤5 =1=1 否 否 是 
22 ≤10≤10 ≤n−1≤n−1 是 
33 ≤15≤15 是 否 否 
44 ≤1000≤1000 =1=1 否 是 
55 ≤30,000≤30,000 是 否 
66 否 
77 ≤n−1≤n−1 是 
88 ≤50,000≤50,000 
99 ≤1,000≤1,000 否 是 是 
1010 ≤30,000≤30,000 
1111 ≤50,000≤50,000 
1212 ≤50≤50 否 
1313 
1414 ≤200≤200 
1515 
1616 ≤1000≤1000 
1717 否 
1818 ≤30,000≤30,000 
1919 
2020 ≤50,000≤50,000 

其中,“分支不超过3”的含义为:每个路口至多有33条道路与其相连。

对于所有的数据,
#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int n,m,t,cnt,l=1,r=0;
int first[N],v[N],w[N],nxt[N];
multiset<int>S;
multiset<int>::iterator it;
void add(int x,int y,int z){
	nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
int f1[N],f2[N];
void dfs(int x,int fa){
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)  continue;
		dfs(to,x);
		if(f1[x]<f1[to]+w[i]){
			f2[x]=f1[x];
			f1[x]=f1[to]+w[i];
		}
		else  if(f2[x]<f1[to]+w[i])
			f2[x]=f1[to]+w[i];
		r=max(r,f1[x]+f2[x]);
	}
}
int f[N],tmp[N];
void dp(int x,int fa,int k){
	for(int i=first[x];i;i=nxt[i])
		if(v[i]!=fa)  dp(v[i],x,k);
	int top=0;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)  continue;
		f[to]+=w[i];
		(f[to]>=k)?(++cnt):(tmp[++top]=f[to]);
	}
	sort(tmp+1,tmp+top+1),S.clear();
	for(int i=1;i<=top;++i){
		it=S.lower_bound(k-tmp[i]);
		if(it!=S.end())  S.erase(it),cnt++;
		else  S.insert(tmp[i]);
	}
	f[x]=S.size()?*S.rbegin():0;
}
bool check(int mid){
	cnt=0,dp(1,0,mid);
	return cnt>=m;
}
int main(){
	int x,y,z;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	dfs(1,0);
	while(l<r){
		int mid=(l+r+1)>>1;
		if(check(mid))  l=mid;
		else  r=mid-1;
	}
	printf("%d\n",l);
	return 0;
}
1907:【18NOIP提高组】旅行

时间限制: 1000 ms         内存限制: 524288 KB
提交数: 606     通过数: 207 

【题目描述】
小 Y 是一个爱好旅行的OIer。她来到X 国,打算将各个城市都玩一遍。

小Y 了解到,X 国的nn 个城市之间有m 条双向道路。每条双向道路连接两个城市。不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且,从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些道路从一个城市前往另一个城市。

小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。

为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将它的编号记录下来。她知道这样会形成一个长度为 nn 的序列。她希望这个序列的字典序最小,你能帮帮她吗?

对于两个长度均为nn 的序列AA 和 BB,当且仅当存在一个正整数 xx,满足以下条件时,我们说序列 AA 的字典序小于 BB。

⚫对于任意正整数 1≤i<x1≤i<x,序列 AA 的第 ii 个元素 AiAi   和序列 BB 的第 ii 个元素 BiBi 相同。

⚫序列 AA 的第 xx 个元素的值小于序列 BB 的第 xx 个元素的值。

【输入】
输入共m+1m+1 行。第一行包含两个整数n,m(m≤n)n,m(m≤n) ,中间用一个空格分隔。接下来mm 行,每行包含两个整数u,v(1≤u,v≤n)u,v(1≤u,v≤n) ,表示编号为uu 和vv 的城市之间有一条道路,两个整数之间用一个空格分隔。

【输出】
输出包含一行, nn 个整数,表示字典序最小的序列。相邻两个整数之间用一个空格分隔。

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

6 6
1 3
2 3
2 5
3 4
4 5
4 6
【样例输出2】

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

见目录下的 travel/travel3.in 和 travel/travel3.ans。这组样例满足m = n − 1 。

数据样例3

【输入输出样例4】

见目录下的 travel/travel4.in 和 travel/travel4.ans。这组样例满足m = n 。

数据样例4

【数据规模与约定】

对于100%的数据和所有样例,1≤n≤50001≤n≤5000且m=n−1m=n−1或m=nm=n。

对于不同的测试点,我们约定数据规模如下:

测试点编号 n=n= m=m= 特殊性质 
1,2,31,2,3 1010 n−1n−1 无 
4,54,5 100100 无 
6,7,86,7,8 10001000 每个城市最多与两个城市相连 
9,109,10 10001000 无 
11,12,1311,12,13 50005000 每个城市最多与三个城市相连 
14,1514,15 50005000 无 
16,1716,17 1010 nn 无 
18,1918,19 100100 无 
20,21,2220,21,22 10001000 每个城市最多与两个城市相连 
23,24,2523,24,25 50005000 无 
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 5005
using namespace std;
int n,m,cnt;
int tmp[N],ans[N],ban[3];
vector<int>e[N];
void dp(int x,int fa){
	tmp[++cnt]=x;
	int Size=e[x].size();
	for(int i=0;i<Size;++i){
		int to=e[x][i];
		if(to==fa)  continue;
		if(x==ban[1]&&to==ban[2]||x==ban[2]&&to==ban[1])  continue;
		dp(to,x);
	}
}
int tot,loop[N],vis[N],pre[N];
bool dfs(int x,int fa){
	vis[x]=1;
	int Size=e[x].size();
	for(int i=0;i<Size;++i){
		int to=e[x][i];
		if(to==fa)  continue;
		if(vis[to]){
			loop[++tot]=to;
			for(int u=x;u!=to;u=pre[u])  loop[++tot]=u;
			return true;
		}
		pre[to]=x;
		if(dfs(to,x))  return true;
	}
	return false;
}
int main(){
	int x,y;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		e[x].push_back(y),e[y].push_back(x);
	}
	for(int i=1;i<=n;++i)  sort(e[i].begin(),e[i].end());
	if(m==n-1)  dp(1,0),memcpy(ans,tmp,sizeof(tmp));
	else{
		dfs(1,0),loop[0]=loop[tot];
		for(int i=1;i<=tot;++i){
			ban[1]=loop[i-1],ban[2]=loop[i],cnt=0,dp(1,0);
			if(i==1)  memcpy(ans,tmp,sizeof(tmp));
			else{
				for(int j=1;j<=n;++j){
					if(ans[j]<tmp[j])  break;
					if(ans[j]>tmp[j])  {memcpy(ans,tmp,sizeof(tmp));break;}
				}
			}
		}
	}
	for(int i=1;i<=n;++i)  printf("%d ",ans[i]);
	return 0;
}
1908: 【18NOIP提高组】填数游戏

时间限制: 1000 ms         内存限制: 524288 KB
提交数: 238     通过数: 88 

【题目描述】
小 D 特别喜欢玩游戏。这一天,他在玩一款填数游戏。

这个填数游戏的棋盘是一个n×mn×m的矩形表格。玩家需要在表格的每个格子中填入一个数字(数字 00 或者数字 11),填数时需要满足一些限制。

下面我们来具体描述这些限制。

为了方便描述,我们先给出一些定义:

*我们用每个格子的行列坐标来表示一个格子,即(行坐标,列坐标)。(注意:行列坐标均从 00 开始编号)

*合法路径 P:一条路径是合法的当且仅当:

1.这条路径从矩形表格的左上角的格子(0,0)(0,0)出发,到矩形的右下角格子(n−1,m−1)(n−1,m−1)结束;

2.在这条路径中,每次只能从当前的格子移动到右边与它相邻的格子,或者从当前格子移动到下面与它相邻的格子。

例如:在下面这个矩形中,只有两条路径是合法的,它们分别是p1p1:(0,0)→(0,1)→(1,1)(0,0)→(0,1)→(1,1)和p2p2:(0,0)→(1,0)→(1,1)(0,0)→(1,0)→(1,1)。



对于一条合法的路径 PP,我们可以用一个字符串w(P)w(P)来表示,该字符串的长度为n+m−2n+m−2,其中只包含字符“RR”或者字符“DD”,第 ii 个字符记录了路径 PP 中第 ii 步的移动方法,“RR”表示移动到当前格子右边与它相邻的格子,“DD”表示移动到当前格子下面与它相邻的格子。例如,上图中对于路径p1p1,有w(P1)="RD"w(P1)="RD";而对于另一条路径p2p2,有w(P2)w(P2) = "DR"。

同时,将每条合法路径 PP 经过的每个格子上填入的数字依次连接后,会得到一个长度为n+m−1n+m−1的 0101 字符串,记为 s(P)s(P)。例如,如果我们在格子(0,0)(0,0)和(1,0)(1,0)上填入数字00,在格子(0,1)(0,1)和(1,1)(1,1)上填入数字 11(见上图红色数字)。那么对于路径p1p1,我们可以得到s(P1)="011"s(P1)="011",对于路径p2p2,有s(P2)="001"s(P2)="001"。

游戏要求小 D 找到一种填数字 0、10、1 的方法,使得对于两条路径p1,P2p1,P2,如果w(P1)>w(P2)w(P1)>w(P2),那么必须s(P1)≤s(P2)s(P1)≤s(P2)。我们说字符串 aa 比字符串 bb 小,当且仅当字符串 aa 的字典序小于字符串bb 的字典序,字典序的定义详见第一题。但是仅仅是找一种方法无法满足小 D 的好奇心,小 D 更想知道这个游戏有多少种玩法,也就是说,有多少种填数字的方法满足游戏的要求?

小 D 能力有限,希望你帮助他解决这个问题,即有多少种填 0、10、1 的方法能满足题目要求。由于答案可能很大,你需要输出答案对109+7109+7取模的结果。

【输入】
输入共一行,包含两个正整数 n、mn、m,由一个空格分隔,表示矩形的大小。其中 nn 表示矩形表格的行数,mm 表示矩形表格的列数。

【输出】
输出共一行,包含一个正整数,表示有多少种填 0、10、1 的方法能满足游戏的要求。注意:输出答案对 109+7109+7 取模的结果。

【输入样例】
2 2
【输出样例】
12
【提示】
【样例解释】



样例输入2】

3 3
【样例输出2】

112
【样例输入3】

5 5
【样例输出3】

7136
【数据规模与约定】

测试点编号 n≤n≤ m≤ 
1∼41∼4 33 33 
5∼105∼10 22 10000001000000 
11∼1311∼13 33 10000001000000 
14∼1614∼16 88 88 
17∼2017∼20 88 10000001000000 
#include<cstdio>
#include<cstring>
#include<algorithm>
#define P 1000000007
using namespace std;
int n,m,inv128=570312504,inv384=190104168;
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	if(n>m)  swap(n,m);
	if(n==1)  printf("%d\n",power(2,m));
	else  if(n==2)  printf("%d\n",mul(4,power(3,m-1)));
	else  if(n==3)  printf("%d\n",mul(112,power(3,m-3)));
	else{
		if(m==n)  printf("%d\n",(83ll*power(8,n)%P+5ll*power(2,n+7)%P)*inv384%P);
		else  printf("%d\n",(83ll*power(8,n)%P+power(2,n+8))*power(3,m-n-1)%P*inv128%P);
	}
	return 0;
}
1909:【18NOIP提高组】保卫王国

时间限制: 2000 ms         内存限制: 524288 KB
提交数: 617     通过数: 130 

【题目描述】
Z 国有nn座城市,n−1n−1条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。

Z 国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:

⚫一座城市可以驻扎一支军队,也可以不驻扎军队。

⚫由道路直接连接的两座城市中至少要有一座城市驻扎军队。

⚫在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是pipi。

小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出了mm个要求,每个要求规定了其中两座城市是否驻扎军队。小 Z 需要针对每个要求逐一给出回答。具体而言,如果国王提出的第jj个要求能够满足上述驻扎条件(不需要考虑第 jj  个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第jj个要求无法满足,则需要输出−1(1≤j≤m)−1(1≤j≤m)。现在请你来帮助小 Z。

【输入】
第 11 行包含两个正整数nn, mm和一个字符串typetype,分别表示城市数、要求数和数据类型。typetype是一个由大写字母 AA,BB 或 CC 和一个数字 1,2,31,2,3 组成的字符串。它可以帮助你获得部分分。你可能不需要用到这个参数。这个参数的含义在【数据规模与约定】中有具体的描述。

第22行nn个整数pipi,表示编号ii的城市中驻扎军队的花费。

接下来n−1n−1行,每行两个正整数u,vu,v,表示有一条uu到vv的双向道路。

接下来mm行,第jj行四个整数a,x,b,y(a≠b)a,x,b,y(a≠b),表示第j个要求是在城市aa驻扎xx支军队,在城市bb驻扎yy支军队。其中,x、yx、y  的取值只有 00 或 11:若 xx 为 00,表示城市 aa 不得驻扎军队,若 xx 为 11,表示城市 aa 必须驻扎军队;若 yy 为 00,表示城市 bb 不得驻扎军队,若 yy 为 11,表示城市 bb 必须驻扎军队。

输入中每一行相邻的两个数据之间均用一个空格分隔。

【输出】
输出共mm行,每行包含 11  个整数,第jj行表示在满足国王第jj个要求时的最小开销,如果无法满足国王的第jj个要求,则该行输出−1−1。

【输入样例】
5 3 C3
2 4 1 3 9
1 5
5 2
5 3
3 4
1 0 3 0
2 1 3 1
1 0 5 0
【输出样例】
12
7
-1
【提示】
【样例解释】

对于第一个要求,在 44 号和 55 号城市驻扎军队时开销最小。

对于第二个要求,在 11 号、22 号、33 号城市驻扎军队时开销最小。

第三个要求是无法满足的,因为在 11 号、55 号城市都不驻扎军队就意味着由道路直接连接的两座城市中都没有驻扎军队。

【输入输出样例2】

数据样例2

【数据规模与约定】

对于100%的数据,n,m≤300000,1≤pi≤100000n,m≤300000,1≤pi≤100000。

测试点编号 typetype n=n= m=m= 
1∼21∼2 A3A3 1010 1010 
3∼43∼4 C3C3 
5∼65∼6 A3A3 100100 100100 
77 C3C3 
8∼98∼9 A3A3 20002000 20002000 
10∼1110∼11 C3C3 
12∼1312∼13 A1A1 100000100000 100000100000 
14∼1614∼16 A2A2 
1717 A3A3 
18∼1918∼19 B1B1 
20∼2120∼21 C1C1 
2222 C2C2 
23∼2523∼25 C3C3 

数据类型的含义:

A:城市ii与城市i+1i+1直接相连。

B:任意城市与城市 11 的距离不超过 100100(距离定义为最短路径上边的数量),即如果这棵树以 11 号城市为根,深度不超过 100100。

C:在树的形态上无特殊约束。

1:询问时保证a=1,x=1a=1,x=1,即要求在城市 11 驻军。对b,yb,y没有限制。

2:询问时保证a,ba,b是相邻的(由一条道路直接连通)

3:在询问上无特殊约束。
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
#define maxn 100010
#define ll long long
#define inf 999999999

int n,m;
char type[5];
int val[maxn];
struct edge{int y,next;};
edge e[2*maxn];
int first[maxn];
void buildroad(int x,int y)
{
	static int len=0;
	e[++len]=(edge){y,first[x]};
	first[x]=len;
}
map<int,map<int,bool> >mmp;
int fa[maxn],size[maxn],mson[maxn];
void dfs1(int x)
{
	size[x]=1;
	for(int i=first[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa[x])continue;
		fa[y]=x;
		dfs1(y);
		size[x]+=size[y];
		if(size[y]>size[mson[x]])mson[x]=y;
	}
}
int now[maxn],old[maxn],id=0,top[maxn];
int be[maxn],bottom[maxn],cnt=0;
void dfs2(int x,int tp,int belong)
{
	now[x]=++id;old[id]=x;top[x]=tp;be[x]=belong;
	if(mson[x]==0){bottom[belong]=x;return;}
	dfs2(mson[x],tp,belong);
	for(int i=first[x];i;i=e[i].next)
	if(e[i].y!=fa[x]&&e[i].y!=mson[x])dfs2(e[i].y,e[i].y,++cnt);
}
ll g[maxn][2][2],f[maxn][2];
/*
g[i][1] , g[i][1]
g[i][0] , inf
*/
void dp(int x)
{
	g[x][0][0]=val[x];g[x][1][1]=inf;
	f[x][1]=val[x];
	for(int i=first[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa[x])continue;
		dp(y);
		f[x][0]+=f[y][1];
		f[x][1]+=min(f[y][0],f[y][1]);
		if(y!=mson[x])
		{
			g[x][0][0]+=min(f[y][0],f[y][1]);
			g[x][1][0]+=f[y][1];
		}
	}
	g[x][0][1]=g[x][0][0];
}
struct matrix{
	ll a[2][2];
	matrix(){memset(a,63,sizeof(a));}
	void set(ll s[2][2])
	{
		for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		a[i][j]=s[i][j];
	}
	matrix operator *(const matrix b)
	{
		matrix c;
		for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		for(int k=0;k<2;k++)
		c.a[i][j]=min(c.a[i][j],a[i][k]+b.a[k][j]);
		return c;
	}
};
struct node{
	int l,r;
	matrix z;
	node *zuo,*you;
	node(int x,int y)
	{
		l=x,r=y;
		if(l<r)
		{
			int mid=l+r>>1;
			zuo=new node(l,mid);
			you=new node(mid+1,r);
			z=zuo->z*you->z;
		}
		else zuo=you=NULL,z.set(g[old[l]]);
	}
	void change(int x)
	{
		if(l==r){z.set(g[old[l]]);return;}
		int mid=l+r>>1;
		if(x<=mid)zuo->change(x);
		else you->change(x);
		z=zuo->z*you->z;
	}
	matrix ask(int x,int y)
	{
		if(l==x&&r==y)return z;
		int mid=l+r>>1;
		if(y<=mid)return zuo->ask(x,y);
		else if(x>=mid+1)return you->ask(x,y);
		else return zuo->ask(x,mid)*you->ask(mid+1,y);
	}
};
node *root=NULL;
/*
g_{i,1} g_{i,1}
g_{i,0} inf
*/
matrix before,after;
void change(int x,int y)
{
	g[x][0][0]+=y;g[x][0][1]=g[x][0][0];
	while(x!=0)
	{
		before=root->ask(now[top[x]],now[bottom[be[x]]]);
		root->change(now[x]);
		after=root->ask(now[top[x]],now[bottom[be[x]]]);
		x=fa[top[x]];
		g[x][0][0]+=min(after.a[0][0],after.a[1][0])-min(before.a[0][0],before.a[1][0]);
		g[x][0][1]=g[x][0][0];
		g[x][1][0]+=after.a[0][0]-before.a[0][0];
	}
}

int main()
{
	scanf("%d %d %s",&n,&m,&type);
	for(int i=1;i<=n;i++)
	scanf("%d",&val[i]);
	for(int i=1,x,y;i<n;i++)
	scanf("%d %d",&x,&y),buildroad(x,y),buildroad(y,x),mmp[x][y]=mmp[y][x]=true;
	dfs1(1);
	dfs2(1,1,++cnt);
	dp(1);
	root=new node(1,n);
	for(int i=1,x,xx,y,yy;i<=m;i++)
	{
		scanf("%d %d %d %d",&x,&xx,&y,&yy);
		if(xx==0&&yy==0&&mmp[x][y])
		{
			printf("-1\n");
			continue;
		}
		ll ans=0;
		if(xx==0)change(x,inf-val[x]);
		else change(x,-inf-val[x]),ans+=inf+val[x];
		if(yy==0)change(y,inf-val[y]);
		else change(y,-inf-val[y]),ans+=inf+val[y];
		
		matrix re=root->ask(now[1],now[bottom[be[1]]]);
		if(x==1)ans+=re.a[(xx^1)][0];
		else if(y==1)ans+=re.a[(yy^1)][0];
		else ans+=min(re.a[0][0],re.a[1][0]);
		printf("%lld\n",ans);
		
		change(x,val[x]-(xx?-inf:inf));change(y,val[y]-(yy?-inf:inf));
	}
}
1986:【19CSPS提高组】格雷码

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 1448     通过数: 549 

【题目描述】
通常,人们习惯将所有 n 位二进制串按照字典序排列,例如所有 2 位二进制串按字典序从小到大排列为:00,01,10,11。

格雷码(Gray Code)是一种特殊的 n 位二进制串排列法,它要求相邻的两个二进制串间恰. 好. 有一位不. 同. ,特别地,第一个串与最后一个串也算作相邻。

所有 2 位二进制串按格雷码排列的一个例子为:00,01,11,10。

n 位格雷码不止一种,下面给出其中一种格雷码的生成算法:

1. 1 位格雷码由两个 1 位二进制串组成,顺序为:0,1。

2. n + 1 位格雷码的前 2n2n 个二进制串,可以由依此算法生成的 n 位格雷码(总共2n2n 个 n 位二进制串)按顺. 序. 排列,再在每个串前加一个前缀 0 构成。

3. n + 1 位格雷码的后 2n2n 个二进制串,可以由依此算法生成的 n 位格雷码(总共2n2n 个 n 位二进制串)按逆. 序. 排列,再在每个串前加一个前缀 1 构成。

综上,n + 1 位格雷码,由 n 位格雷码的 2n2n 个二进制串按顺序排列再加前缀 0,和按逆序排列再加前缀 1 构成,共 2n+12n+1 个二进制串。另外,对于 n 位格雷码中的 2n2n 个二进制串,我们按上述算法得到的排列顺序将它们从 0 ∼ 2n2n − 1 编号。

按该算法,2 位格雷码可以这样推出:

1. 已知 1 位格雷码为 0,1。

2. 前两个格雷码为 00,01。后两个格雷码为 11,10。合并得到 00,01,11,10,编号依次为 0 ∼ 3。

同理,3 位格雷码可以这样推出:

1. 已知 2 位格雷码为:00,01,11,10。

2. 前四个格雷码为:000,001,011,010。后四个格雷码为:110,111,101,100。合并得到:000,001,011,010,110,111,101,100,编号依次为 0 ∼ 7。

现在给出 n, k,请你求出按上述算法生成的 n 位格雷码中的 k 号二进制串。

【输入】
仅一行两个整数 n, k,意义见题目描述。

【输出】
仅一行一个 n 位二进制串表示答案。

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

2 位格雷码为:00,01,11,10,编号从 0 ∼ 3,因此 3 号串是 10。

【样例 2 输入】

3 5
【样例 2 输出】

111
【样例 2 解释】

3 位格雷码为:000,001,011,010,110,111,101,100,编号从 0 ∼ 7,因此 5号串是 111。

【数据范围】

对于 50% 的数据:n≤10n≤10;

对于 80% 的数据:k≤5×106k≤5×106;

对于 95% 的数据:k≤263−1k≤263−1;

对于 100% 的数据:1≤n≤64,0≤k<2n1≤n≤64,0≤k<2n。
#include <cstdio>
#include <iostream>
#include <algorithm>
#define ull unsigned long long
inline ull ksm(ull x,ull y)
{
	ull ans=1ll;
	while(y)
	{
		if(y&1) ans=(ull)(ans*x);
		x=x*x;
		y/=2ll;
	}
	return ans;
}
void dfs(ull n,ull k)
{
	if(n==1)
	{
		if(k==0) printf("0");
		else printf("1");
		return;
	}
	ull s=ksm(2,n-1)-1;
	//printf("%llu %llu\n",s,k);
	if(k<=s)
	{
	    printf("0");
		dfs(n-1,k);
	}
	else 
	{
	    printf("1");
		dfs(n-1,s-(k-s)+1);
	}
}
inline ull read()
{
    ull x=0;
    char ch=0;
    while(ch<'0'||ch>'9')  ch=getchar();
    while(ch>='0'&&ch<='9')
     x=x*10ll+1ll*(ch-'0'),ch=getchar();
    return x;
}
int main()
{
	ull n,k;
	n=read(),k=read();
	dfs(n,k);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值