NOIP2017题解

小凯的疑惑

下面没有证明,只有结论

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<queue>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in",,"r",stdin);freopen(a".out","w",stdout)
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return f*sum;
}
int a,b;
int main(){
	a=gi();b=gi();
	printf("%lld
",1ll*a*b-a-b);
	return 0;
}

时间复杂度

没什么好说的,大模拟即可,而且没有细节.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=110;
char Time[110];int bl[N],Flag[N],top,jz[N],A[N],B[N],Goto[N],id[N],from[N],into[N];
int gettime();
char get();
char getFE();
int main(){
	int T=gi();
	while(T--){
		int L=gi();scanf("%s",Time+1);
		int t=gettime();int flag=1;top=0;
		memset(Flag,0,sizeof(Flag));
		for(int i=1;i<=L;i++){
			char ch=getFE();
			if(ch=='E'){jz[i]=0;if(!top)flag=0;else{Flag[bl[top]]=0;Goto[id[top]]=i;from[i]=id[top];top--;}continue;}
			bl[++top]=get()-'a';id[top]=i;if(Flag[bl[top]])flag=0;Flag[bl[top]]=1;
			ch=getchar();int x=0,y=0;
			while((ch<'0' || ch>'9') && ch!='n')ch=getchar();
			if(ch=='n')x=101;
			else while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
			ch=getchar();
			while((ch<'0' || ch>'9') && ch!='n')ch=getchar();
			if(ch=='n')y=101;
			else while(ch>='0' && ch<='9'){y=y*10+ch-'0';ch=getchar();}
			jz[i]=1;A[i]=x;B[i]=y;
		}
		if(top)flag=0;
		if(!flag){puts("ERR");continue;}
		int Mx=0,now=0;
		for(int i=1;i<=L;i++){
			if(jz[i]){
				if(A[i]>B[i]){i=Goto[i];continue;}
				if(B[i]<101 || A[i]==101)continue;
				now++;into[i]=1;
			}
			else
				if(into[from[i]]){into[from[i]]=0;now--;}
			Mx=max(Mx,now);
		}
		
		if(Mx==t)puts("Yes");
		else puts("No");
	}
	return 0;
}
char getFE(){
	char ch=getchar();
	while(ch!='F' && ch!='E')ch=getchar();
	return ch;
}
int gettime(){
	if(Time[3]=='1')return 0;
	int ret=0,pos=5;
	while(Time[pos]>='0' && Time[pos]<='9'){ret=ret*10+Time[pos]-'0';pos++;}
	return ret;
}
char get(){
	char ch=getchar();
	while(ch<'a' || ch>'z')ch=getchar();
	return ch;
}

逛公园

看到(k)的范围不大,想一下(dp).

(dp_{i,j})表示到了(i)号点,与标准最短路的差为(j)的方案,记忆化搜索即可.运用一条路径更新的时候应该是要求反图的最短路.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> >q;
#define mp make_pair
const int N=200010;
int dis[N],vis[N],dp[N][60],n,front[N],cnt,m,p,k,Vis[N][60];
struct node{int to,nxt,w;}e[N<<1];
void Add(int u,int v,int w){e[++cnt]=(node){v,front[u],w};front[u]=cnt;}
void dijkstra(){
	memset(dis,63,sizeof(dis));dis[n]=0;memset(vis,0,sizeof(vis));
	q.push(mp(0,n));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u])continue;vis[u]=1;
		for(int i=front[u];i;i=e[i].nxt)
			if(i%2==0){
				int v=e[i].to;
				if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;q.push(mp(dis[v],v));}
			}
	}
}
int flag,Time;
int dfs(int u,int K){
	if(Vis[u][K]==Time){flag=1;return 0;}
	if(~dp[u][K])return dp[u][K];
	Vis[u][K]=Time;int sum=0;
	for(int i=front[u];i;i=e[i].nxt)
		if(i&1){
			int v=e[i].to,kk=K-(dis[v]-dis[u]+e[i].w);
			if(kk<0 || kk>k)continue;
			sum=(sum+dfs(v,kk))%p;
			if(flag)return 0;
		}
	Vis[u][K]=Time-1;
	if(u==n && !K)sum=1;
	return dp[u][K]=sum;
}
#undef mp
int main(){
	int T=gi();
	while(T--){
		memset(front,0,sizeof(front));cnt=0;
		memset(dp,-1,sizeof(dp));
		n=gi();m=gi();k=gi();p=gi();
		for(int i=1;i<=m;i++){int u=gi(),v=gi(),w=gi();Add(u,v,w);Add(v,u,w);}
		dijkstra();int ans=0;flag=0;
		for(int i=0;i<=k;i++)
		{
			Time++;
			ans=(ans+dfs(1,i))%p;
		}
		if(!flag)printf("%d
",ans);
		else puts("-1");
	}
	return 0;
}

奶酪

直接(n^2)枚举两个点然后判断连通性,判断每一个奶酪与上下的连通性,用并查集维护即可.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=1010;
int f[N],n,h,r;
int find(int x){if(f[x]!=x)f[x]=find(f[x]);return f[x];}
struct node{double x,y,z;}c[N];
double dis(int i,int j){
	return sqrt((c[i].x-c[j].x)*(c[i].x-c[j].x)+(c[i].y-c[j].y)*(c[i].y-c[j].y)+(c[i].z-c[j].z)*(c[i].z-c[j].z));
}
int main(){
	int T=gi();
	while(T--){
		n=gi();h=gi();r=gi();
		for(int i=0;i<=n+1;i++)f[i]=i;
		for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].z);
		for(int i=1;i<=n;i++)if(c[i].z<=r)f[find(i)]=0;
		for(int i=1;i<=n;i++)if(c[i].z>=h-r)f[find(i)]=n+1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(i!=j && find(i)!=find(j) && dis(i,j)<=2*r)
					f[find(i)]=find(j);
		if(find(0)==find(n+1))puts("Yes");
		else puts("No");
	}
	return 0;
}

宝藏

(n)的只有12,可以往状压方面想.

不妨设(f_s)表示当前状态为(s)时的最小代价,那么更新就是选一个在(s)里面的点,然后扩展一个不在(s)里面的点,每一次钦定一个开始点即可.记住要用邻接矩阵,不然边数是(2000)而非(n^2).

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=13;
int n,m,front[N],cnt,g[N][N];
int dp[1<<N],dep[N];
void dfs(int s){
	for(int u=0;u<n;u++)
		if(s&(1<<u))
			for(int v=0;v<n;v++)
				if(g[u][v]!=g[n][n] && !(s&(1<<v))){
					if(dp[s^(1<<v)]>dp[s]+g[u][v]*dep[u]){
						int tmp=dep[v];dep[v]=dep[u]+1;
						dp[s^(1<<v)]=dp[s]+g[u][v]*dep[u];
						dfs(s^(1<<v));
						dep[v]=tmp;
					}
				}
}
int main(){
	n=gi();m=gi();memset(g,63,sizeof(g));
	for(int i=1;i<=m;i++){
		int u=gi(),v=gi(),w=gi();
		u--;v--;
		g[u][v]=g[v][u]=min(g[u][v],w);
	}
	int ans=1e9+10;
	for(int i=0;i<n;i++){
		memset(dp,63,sizeof(dp));
		dp[1<<i]=0;dep[i]=1;
		dfs(1<<i);
		ans=min(ans,dp[(1<<n)-1]);
	}
	printf("%d
",ans);
	return 0;
}

列队

考虑每一次离队变化((x,y))改变的只有第(x)行和最后一列.那么我们对于每一行的前(m-1)个维护一个线段树,最后一列维护一个线段树,然后在对于上面提到的每一个维护一个(vector)存储进队的编号.然后线段树每一次查询然后放到(vector)里面就可以了.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=600010;
int n,m,Max,tot,rt[N];
vector<ll>v[N];
struct node{int ls,rs,sum;}t[N<<4];
void update(int&o,int l,int r,int pos){
	if(!o)o=++tot;t[o].sum++;
	if(l==r)return;int mid=(l+r)>>1;
	if(pos<=mid)update(t[o].ls,l,mid,pos);
	else update(t[o].rs,mid+1,r,pos);
}
int query(int o,int l,int r,int v){
	if(l==r)return l;
	int mid=(l+r)>>1,got=mid-l+1-t[t[o].ls].sum;
	if(got>=v)return query(t[o].ls,l,mid,v);
	else return query(t[o].rs,mid+1,r,v-got);
}
ll solve2(int,ll);
ll solve1(int,int);
int main(){
	n=gi();m=gi();int Q=gi();Max=max(n,m)+Q;
	while(Q--){
		int x=gi(),y=gi();
		if(y==m)printf("%lld
",solve2(x,0));
		else printf("%lld
",solve1(x,y));
	}
	return 0;
}
ll solve2(int x,ll y){
	int pos=query(rt[n+1],1,Max,x);update(rt[n+1],1,Max,pos);
	ll ans=pos<=n?1ll*pos*m:v[n+1][pos-n-1];
	v[n+1].push_back(y?y:ans);
	return ans;
}
ll solve1(int x,int y){
	int pos=query(rt[x],1,Max,y);update(rt[x],1,Max,pos);
	ll ans=pos<m?1ll*(x-1)*m+pos:v[x][pos-m];
	v[x].push_back(solve2(x,ans));
	return ans;
}
原文地址:https://www.cnblogs.com/mleautomaton/p/10962759.html