NOIP2016

玩具谜题

Link
模拟。

#include<iostream>
#include<vector>
using namespace std;
int n,m,p;
vector<string> work;
vector<bool> a;
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        bool tmp;
		string tmp1;
        cin>>tmp>>tmp1;
        a.push_back(tmp);
        work.push_back(tmp1);
	}
    while(m--)
    {
        bool tmp;
        int num;
        cin>>tmp>>num;
        if(num)
            if(a[p]^tmp)
                p=(p+num)%n;
            else
                p=(p-num%n+n)%n;

    } 
    cout<<work[p];
    return 0;
}

换教室

Link
先floyd求最短路。然后dp。
\(f_{i,j,0/1}\)表示前\(i\)节课,用了\(j\)次换教室的机会,这次换不换的最小答案。
转移就非常显然了。

#include<bits/stdc++.h>
#define db double
using namespace std;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int dis[307][307];db p[2007],f[2007][2007],g[2007][2007];int c[2007],d[2007];
db Dis(int i,db a,db b){return dis[c[i-1]][c[i]]*a*b+dis[c[i-1]][d[i]]*a*(1-b)+dis[d[i-1]][c[i]]*(1-a)*b+dis[d[i-1]][d[i]]*(1-a)*(1-b);}
int main()
{
    int n=read(),m=read(),V=read(),E=read();db ans=1e9;
    memset(dis,0x3f,sizeof dis),memset(f,0x7f,sizeof f),memset(g,0x7f,sizeof g);
    for(int i=1;i<=V;++i) dis[i][i]=0;
    for(int i=1;i<=n;++i) c[i]=read();
    for(int i=1;i<=n;++i) d[i]=read();
    for(int i=1;i<=n;++i) scanf("%lf",&p[i]),p[i]=1-p[i];
    for(int i=1,u,v,w;i<=E;++i) u=read(),v=read(),w=read(),dis[u][v]=dis[v][u]=min(dis[u][v],w);
    for(int k=1,i,j;k<=V;++k) for(i=1;i<=V;++i) for(j=1;j<=V;++j) dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
    f[1][0]=g[1][1]=0;
    for(int i=2,j;i<=n;++i)
    {
	f[i][0]=f[i-1][0]+Dis(i,1,1);
	for(j=1;j<=m&&j<=i;++j)
	{
	    f[i][j]=min(f[i-1][j]+Dis(i,1,1),g[i-1][j]+Dis(i,p[i-1],1));
	    g[i][j]=min(f[i-1][j-1]+Dis(i,1,p[i]),g[i-1][j-1]+Dis(i,p[i-1],p[i]));
	}
    }
    for(int i=0;i<=m;++i) ans=min(ans,min(f[n][i],g[n][i]));
    printf("%.2lf",ans);
}

天天爱跑步

Link
对于一条路径\((u,v)\),设\(l=lca(u,v)\)
它会对\((u,l)\)上满足\(dep_x+t_x=dep_u\)的点产生一点贡献。
\((l,u)\)上满足\(t_x-dep_x=dep_u-2dep_l\)的点产生一点贡献。
我们可以每个点开个两个桶,对于一条路径\((u,l,v)\),往\((u,l)\)上的点的一号桶中插入\(dep_u\),往\((l,v)\)\(l\)除外)上的点的二号桶中插入\(dep_u-2dep_l\)
最后每个点的答案就是一号桶中\(dep_x+t_x\)和二号桶中\(t_x-dep_x\)的个数和。
注意到第二个桶可能爆负下标,全部右移\(n\)就行了。
注意到我们的加是对树上一条链加,因此我们可以差分转化为单点加,最后再还原。
但是每个点开一个桶是开不下的,所以我们考虑只开一个桶,一次dfs求出所有答案。
一种优秀的方法是:每个点访问到时先把其答案减去桶中对应数,遍历完其子树之后再加上桶中对应数。
因为你访问到这个点时桶中是有外面的信息的。而所有会产生贡献的信息是该点子树中所有差分数组之和。而我们遍历完该点子树的时候桶的信息就是外面的信息加上子树中的信息。因此这样做是正确的。

#include<bits/stdc++.h>
#define pb push_back
#define pi pair<int,int>
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
    void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put(' ');}
}
using namespace IO;
const int N=300007,A=300000;
vector<int>E[N];vector<pi>opt1[N],opt2[N];int fa[N],dep[N],size[N],son[N],top[N],ans[N],w[N],cnt1[N<<1],cnt2[N<<1];
void dfs(int u){for(int v:E[u])if(v^fa[u])dep[v]=dep[u]+1,fa[v]=u,size[v]=1,dfs(v),size[u]+=size[v],son[u]=size[v]>size[son[u]]? v:son[u];}
void dfs(int u,int tp){top[u]=tp;if(son[u]) dfs(son[u],tp);for(int v:E[u]) if(v^son[u]&&v^fa[u]) dfs(v,v);}
int lca(int u,int v){while(top[u]^top[v]) dep[top[u]]>dep[top[v]]? u=fa[top[u]]:v=fa[top[v]];return dep[u]<dep[v]? u:v;}
void ins(int v,int u){int l=lca(u,v);opt1[u].pb(pi(dep[u],1)),opt1[l].pb(pi(dep[u],-1)),opt2[v].pb(pi(dep[u]-dep[l]*2,1)),opt2[fa[l]].pb(pi(dep[u]-dep[l]*2,-1));}
void cal(int u)
{
    ans[u]-=cnt1[dep[u]+w[u]]+cnt2[w[u]-dep[u]+A];
    for(int v:E[u]) if(v^fa[u]) cal(v);
    for(pi x:opt1[u]) cnt1[x.first]+=x.second;
    for(pi x:opt2[u]) cnt2[x.first+A]+=x.second;
    ans[u]+=cnt1[dep[u]+w[u]]+cnt2[w[u]-dep[u]+A];
}
int main()
{
    int n=read(),m=read();
    for(int i=1,u,v;i<n;++i) u=read(),v=read(),E[u].pb(v),E[v].pb(u);
    dfs(dep[1]=size[1]=1),dfs(1,1);
    for(int i=1;i<=n;++i) w[i]=read();
    for(int i=1;i<=m;++i) ins(read(),read());
    cal(1);
    for(int i=1;i<=n;++i) write(ans[i]);
    return Flush(),0;
}

组合数问题

Link
模拟。

#include<bits/stdc++.h>
using namespace std;
const int N=2001;
int C[N][N],sum[N][N];
inline int read()
{
    register int x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9')
        ch=getchar();
    while(ch>='0'&&ch<='9')
        x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x;
}
int main()
{
	register int t=read(),k=read();
	for(register int i=0;i<=2000;++i)
	{
		C[i][0]=1;
		for(register int j=1;j<=i;++j)
			C[i][j]=(C[i-1][j]+C[i-1][j-1]>=k? C[i-1][j]+C[i-1][j-1]-k:C[i-1][j]+C[i-1][j-1]);
	}
	for(register int i=2;i<=2000;++i)
	{
		for(register int j=1;j<=i;++j)
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(C[i][j]==0);
		sum[i][i+1]=sum[i][i];
	}
	while(t--)
	{
		register int n=read(),m=read();
		if(m>n)
			m=n;
		cout<<sum[n][m]<<endl;
	}
	return 0;
}

蚯蚓

Link
我们知道这样一件事:
在更靠前的时刻被切开形成的两条蚯蚓一定比在靠后的时刻切开形成的长。
所以我们就有了这样一个做法:
开三个队列分别记录:
最开始的蚯蚓、每一次被切开形成的长的蚯蚓、每一次被切开形成的短的蚯蚓。
最开始的蚯蚓先降序排序一下。
每次取出三个队头中最大的一个,把它弹出队列,再把切开形成的蚯蚓压入后两个队列的队尾。
而除了被切开形成的以外的蚯蚓变长,我们可以看做所有蚯蚓变长,被切开形成的变短。
因此记录一个全局变长量就行了。
最后把三个队列归并排序一下输出。

#include<bits/stdc++.h>
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
    void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put(' ');}
}
using namespace IO;
deque<int>q[3];vector<int>ans;
int Find()
{
    int mx=-2e9,id=-1;
    if(!q[0].empty()&&q[0].front()>mx) id=0,mx=q[0].front();
    if(!q[1].empty()&&q[1].front()>mx) id=1,mx=q[1].front();
    if(!q[2].empty()&&q[2].front()>mx) id=2,mx=q[2].front();
    return id;
}
int main()
{
    int n=read(),m=read(),Q=read(),u=read(),v=read(),t=read(),d=0,p,x,a,b;
    for(int i=1;i<=n;++i) q[0].push_back(read());
    sort(q[0].begin(),q[0].end(),greater<int>());
    for(int i=1;i<=m;++i)
    {
	p=Find(),x=q[p].front()+d,q[p].pop_front();
	if(!(i%t)) write(x);
	d+=Q,a=1ll*x*u/v,b=x-a;
	if(a<b) swap(a,b);
	q[1].push_back(a-d),q[2].push_back(b-d);
    }Put('\n');
    while(q[0].size()+q[1].size()+q[2].size()) p=Find(),ans.push_back(q[p].front()+d),q[p].pop_front();
    for(int i=1;i<=n+m;++i) if(!(i%t)) write(ans[i-1]);
    return Flush(),0;
}

愤怒的小鸟

Link
状压dp。
先预处理一下经过两点的抛物线上的点的集合。
对于一个集合\(S\),确定一个最小的不在该集合内的\(i\),再枚举一个\(j\),进行转移。

#include<bits/stdc++.h>
#define db long double
using namespace std;
const db eps=1e-10;
int t,n,m,S[19][19],low[1<<18],f[1<<18];
struct node{db x,y;}p[19];
void cal(db &a,db &b,db x,db y,db X,db Y){b=(Y*x*x-y*X*X)/((X*x)*(x-X)),a=(y-b*x)/(x*x);}
int min(int a,int b){return a<b? a:b;}
int main()
{
    int n,m,i,j,k;db a,b;
    for(i=0;i<1<<18;low[i]=j,++i) for(j=1;j<=18&&i&1<<j-1;++j);
    for(scanf("%d",&m);m;--m)
    {
	memset(S,0,sizeof S),memset(f,0x3f,sizeof f),f[0]=n=0,scanf("%d%d",&n,&i);
        for(i=1;i<=n;++i) scanf("%Lf%Lf",&p[i].x,&p[i].y),S[i][i]=1<<i-1;
        for(i=1;i<=n;++i)
            for(j=1;j<=n;++j)
	    {
                if(fabs(p[i].x-p[j].x)<eps) continue;
                cal(a,b,p[i].x,p[i].y,p[j].x,p[j].y);
                if(a>-eps) continue;
                for(k=1;k<=n;++k) if(fabs((a*p[k].x+b)*p[k].x-p[k].y)<eps) S[i][j]|=1<<k-1;
            }
        for(i=0;i<1<<n;++i) for(j=low[i],k=1;k<=n;++k) f[i|S[j][k]]=min(f[i|S[j][k]],f[i]+1);
        printf("%d\n",f[(1<<n)-1]);
    }
}
posted @ 2019-11-14 09:39  Shiina_Mashiro  阅读(124)  评论(0编辑  收藏  举报