NOIp2017 题解

版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/82831384

Day1

T1 小凯的疑惑

题目传送门

考场上打表搞了一个很奇怪的结论,化简后就是 a b a b a*b-a-b 。具体证明现在还是不大会。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL a,b;
int main(){
    scanf("%lld%lld",&a,&b);
    if (a>b) swap(a,b);
    if (a==1){
        printf("0\n");
        return 0;
    }
    if (a==2){
        printf("%lld\n",b-2);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    LL ans=(4+(a-3)*2+4)*(a-2)/2+1;
    ans+=(b-a-1)*(a-1);
    printf("%lld\n",ans);
    return 0;
}

T2 时间复杂度

题目传送门

一般用一个栈模拟,细节比较多。没什么好说的。

考场上写的代码只会统计第一个循环。。。然而还是有80。

当时太紧张就写得很丑。

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100
using namespace std;
struct Orz{
    char c;
    bool f;
}stack[MAXN+5];
int top,t,l,cmp,a[MAXN+5],ma;
int f;
char s[MAXN+5],sc[MAXN+5],sx[MAXN+5],sy[MAXN+5];
int main(){
    scanf("%d",&t);
    while (t--){
        memset(a,0,sizeof(a));
        f=0; top=0; cmp=0; ma=0;
        scanf("%d%s",&l,s);
        if (s[2]=='n') f=1;
        for (int i=0;s[i];i++)
            if (s[i]>='0'&&s[i]<='9')
                cmp=cmp*10+s[i]-48;
        int num=0;
        bool flag=false,fa=false,fl=false; if (l&1) flag=true;
        for (int i=1;i<=l;i++){
            int ff=0; char ss[2];
            scanf("%s",ss);
            ff=ss[0];
            if (ff=='F'){
                int c,x=0,y=0;
                scanf("%s%s%s",sc,sx,sy);
                c=sc[0];
                for (int j=0;sx[j];j++)
                    if (sx[j]>='0'&&sx[j]<='9')
                        x=x*10+sx[j]-48;
                for (int j=0;sy[j];j++)
                    if (sy[j]>='0'&&sy[j]<='9')
                        y=y*10+sy[j]-48;
                if (sx[0]=='n') x='n';
                if (sy[0]=='n') y='n';
                if (flag) continue;
                if (a[c-'a']){ flag=true; continue; }
                if (x=='n'&&y!='n') fl=true;
                if (x!='n'&&y!='n'&&x>y) fl=true;
                if (x!='n'&&y=='n') num++;
                if (x!='n'&&y=='n'&&(!fl)) fa=true,ma=max(ma,num);
                a[c-'a']++; 
                Orz p; p.c=c;
                if (x!='n'&&y=='n') p.f=true;
                else p.f=false;
                stack[++top]=p;
            }
            else{
                if (!top) { flag=true; continue; }
                if (stack[top].f) num--;
                a[stack[top--].c-'a']--;
                if (!top) fl=false;
            }
        }
        if (top) flag=true;
        if (flag){
            printf("ERR\n");
            continue;
        }
        if ((fa&&(f==0))||(ma!=cmp&&(f==1))){
            printf("No\n");
            continue;
        }
        printf("Yes\n");
    }
    return 0;
}

T3 逛公园

题目传送门

最短路+记忆化搜索

考场上用最短路优化一下就有60了。。。

扫描二维码关注公众号,回复: 3405535 查看本文章

先刷一遍反图最短路求出每个点到 n n 的最短路 d i s [ i ] dis[i]

f [ i ] [ j ] f[i][j] 表示到第 i i 个点,超出最短路的路程 j \leq j 的方案数。搜索的时候对每条边的转移 f [ i ] [ j ] = f [ v ] [ j d ] f[i][j]=\sum f[v][j-d] 。其中 d = d i s [ v ] d i s [ i ] + w d=dis[v]-dis[i]+w 。判0环的话就记录一下当前状态有没有在访问路径上即可。

代码:

#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
struct P{ int x,d; };
struct edge{ int nxt,to,d; }ed[N<<2];
int n,m,k,p,K,d[N],h1[N],h2[N],f[N][55];
priority_queue<P> q; bool v[N][55];
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0; char ch=readc();
	while (!isdigit(ch)) ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x;
}
#define add(h,x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k
F bool operator <(P a,P b){ return a.d>b.d; }
F void Dij(){
	while (!q.empty()) q.pop();
	for (q.push((P){n,d[n]=0});!q.empty();q.pop()){
		int x; if (q.top().d>d[x=q.top().x]) continue;
		for (int i=h2[x],v;i;i=ed[i].nxt)
			if (d[v=ed[i].to]>d[x]+ed[i].d)
				d[v]=d[x]+ed[i].d,q.push((P){v,d[v]});
	}
}
int dfs(int x,int k){
	if (v[x][k]) return -1; if (f[x][k]) return f[x][k];
	v[x][k]=true,f[x][k]+=(x==n);
	for (int i=h1[x],v,w;i;i=ed[i].nxt)
		if ((w=d[v=ed[i].to]-d[x]+ed[i].d)<=k)
			if (dfs(v,k-w)==-1) return -1;
			else (f[x][k]+=f[v][k-w])%=p;
	return v[x][k]=false,f[x][k];
}
int main(){
	for (int t=_read();t;t--){
		n=_read(),m=_read(),K=_read(),p=_read(),k=0;
		for (int i=1;i<=n;i++) h1[i]=h2[i]=0,d[i]=1e9;
		for (int i=1;i<=n;i++)
			for (int j=0;j<=K;j++) f[i][j]=v[i][j]=0;
		for (int i=1,x,y,z;i<=m;i++){
			x=_read(),y=_read(),z=_read();
			add(h1,x,y,z),add(h2,y,x,z);
		}
		Dij(),printf("%d\n",dfs(1,K));
	}
	return 0;
}

Day2

T1 奶酪

题目传送门

并查集/BFS都可以。本来会爆long long的但是出题人没有卡。判断的时候移个项就好了

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1000
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long LL;
struct edge{
    int next,to;
}ed[MAXN*MAXN*6+5];
struct node{
    LL x,y,z;
}a[MAXN*2+5];
int t,n,k;
LL m,r;
int h[MAXN*2+5],que[MAXN*2+5];
bool f[MAXN*2+5];
void addedge(int x,int y){
    ed[++k].next=h[x]; ed[k].to=y; h[x]=k;
}
LL calc(node a,node b){
    return sqr(a.x-b.x)+sqr(a.y-b.y)+sqr(a.z-b.z);
}
bool bfs(int s,int t){
    memset(f,false,sizeof(f));
    int r=0,w=1; que[1]=s; f[s]=true;
    while (r<w){
        int x=que[++r];
        for (int i=h[x];i;i=ed[i].next)
            if (!f[ed[i].to]){
                que[++w]=ed[i].to;
                f[ed[i].to]=true;
            }
    }
    return f[t];
}
int main(){
    scanf("%d",&t);
    while (t--){
        memset(h,0,sizeof(h)); k=0;
        scanf("%d%lld%lld",&n,&m,&r);
        for (int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z);
            if (a[i].z<=r) addedge(MAXN+2,i);
            if (a[i].z+r>=m) addedge(i,MAXN+1); 
        }
        for (int i=1;i<n;i++)
            for (int j=i+1;j<=n;j++)
                if (calc(a[i],a[j])<=sqr(r*2))
                    addedge(i,j),addedge(j,i);
        if (bfs(MAXN+2,MAXN+1)) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

T2 宝藏

题目传送门

状压DP

为什么我会觉得prim是对的!为什么我70分的暴力都不打!果然我好菜

我是不会告诉你我不知道怎么枚子集的

n=12就是状压的范围。据说标算 O ( 4 n ) O(4^n)​ 我不会啊,优化过的 O ( n 3 n ) O(n3^n)​ 我也不会啊,我只会 O ( n 2 3 n ) O(n^23^n)​ 不管了能过就行

Orz写模拟退火的

f [ i ] [ j ] [ s ] f[i][j][s] 表示以 i i 为根的子树中, i i 的深度的为 j j ,子树中的节点集合为 s s 的最小代价。那么有 f [ i ] [ j ] [ s ] = m i n { f [ k ] [ j + 1 ] [ s s ] + f [ i ] [ j ] [ s s s ] + a [ i ] [ k ] } f[i][j][s]=min\{f[k][j+1][ss]+f[i][j][s-ss]+a[i][k]\} ,其中 s s ss s s 的子集, k k s s ss 内的一个点。时间复杂度为 O ( n 3 3 n ) O(n^33^n)

把转移方程中与 s s 无关的提取出来,预处理 g [ i ] [ j ] [ s ] = m i n { f [ k ] [ j + 1 ] [ s ] + a [ i ] [ k ] } g[i][j][s]=min\{f[k][j+1][s]+a[i][k]\} ,这样就是 O ( n 2 3 n ) O(n^23^n) 的了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 15
#define M (1<<12)+1
using namespace std;
int n,m,ans=1e9,a[N][N],f[N][N][M],g[N][N][M];
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++){
			a[i][j]=1e9;
			for (int s=1;s<1<<n;s++)
				if (s!=1<<i-1) f[i][j][s]=g[i][j][s]=1e9;
		}
	for (int i=1,x,y,z;i<=m;i++)
		scanf("%d%d%d",&x,&y,&z),a[x][y]=a[y][x]=min(a[x][y],z);
	for (int j=n-1;j;j--)
	for (int i=1;i<=n;i++)
	for (int s=1;s<1<<n;s++){
		for (int k=1;k<=n;k++)
			if (((1<<k-1)&s)&&a[i][k]!=1e9)
				g[i][j][s]=min(g[i][j][s],f[k][j+1][s]+a[i][k]*j);
		for (int ss=s&s-1;ss;ss=ss-1&s)
			f[i][j][s]=min(f[i][j][s],f[i][j][s^ss]+g[i][j][ss]);
	}
	for (int i=1;i<=n;i++) ans=min(ans,f[i][1][(1<<n)-1]);
	return printf("%d\n",ans),0;
}

T3 列队

题目传送门

线段树动态开点

去年不会动态开点啊。。。
建n+1棵树,前n棵维护当前这行(除了最后一列),第n+1棵维护最后一列。每次操作的时候分操作的列是不是最后一列讨论一下就好了。
细节有点多,调了好久最后还是借鉴了一下题解

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define F inline
using namespace std;
typedef long long LL;
struct tree{ int ls,rs,sz; LL x; }t[N<<6];
int n,m,q,mx,nd,rt[N],p[N]; LL ans,tmp;
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0; char ch=readc();
	while (!isdigit(ch)) ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x;
}
F void writec(LL x){ if (x>9) writec(x/10); putchar(x%10+48); }
#define pd(l,r,f) (f==n+1?max(min(r,n)-l+1,0):max(min(r+1,m)-l,0))
F void nsrt(int &x,int l,int r,int p,LL w,int f){
	if (!x) t[x=++nd].sz=pd(l,r,f); int mid=l+r>>1;
	t[x].sz++; if (l==r) return void(t[x].x=w);
	if (p<=mid) nsrt(t[x].ls,l,mid,p,w,f);
	else nsrt(t[x].rs,mid+1,r,p,w,f);
}
F LL srch(int &x,int l,int r,int w,int f){
	if (!x) t[x=++nd].sz=pd(l,r,f); int mid=l+r>>1,p; t[x].sz--;
	if (l==r) return t[x].x?t[x].x:t[x].x=(f<=n?1ll*(f-1)*m+l:1ll*l*m);
	if (w<=(p=t[x].ls?t[t[x].ls].sz:mid-l+1))
		return srch(t[x].ls,l,mid,w,f);
	else return srch(t[x].rs,mid+1,r,w-p,f);
}
F LL calc(int x,int y){
	LL ans,tmp=srch(rt[n+1],1,mx,x,n+1);
	ans=y!=m?srch(rt[x],1,mx,y,x):tmp;
	if (y!=m) nsrt(rt[x],1,mx,++p[x],tmp,x);
	return nsrt(rt[n+1],1,mx,++p[n+1],ans,n+1),ans;
}
int main(){
	n=_read(),m=_read(),q=_read(),mx=max(n,m)+q;
	for (int i=1;i<=n+1;i++) p[i]=(i==n+1?n:m-1);
	for (int x,y;q;q--) x=_read(),y=_read(),writec(calc(x,y)),puts("");
	return 0;
}

NOIP2018要苟住啊

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/82831384