Noip2016day1day2考试总结合并

没来的及写上一次的就水掉了

%%%%%考场AK的肥佬

day1

T1 玩具谜题

1e5的复杂度直接取模跳就好了没有那么多技巧性

但是有一点需要注意啊

要么就从0开始输入

要么要注意Mod n之后是否为0,要不会死的不明不白,输出0(但是似乎没有这个东西)

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=1e5+5;

int n,m;

struct point
{
	int loc,id;
	char name[15];
}p[maxn];

int main()
{
	scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++)
    {
    	scanf("%d",&p[i].loc);
    	scanf("\n");
    	cin>>p[i].name;
    	p[i].id=i;
    }
    int pos=p[0].id;
    for (int i=1;i<=m;i++)
    {
    	int pd,len;
    	scanf("%d%d",&pd,&len);
    	if (pd==0)
        {
        	if (p[pos].loc==1)
        	{
        		pos=(pos+len)%n;
        	}
        	else
        	{
        		pos=(pos+n-len)%n;
        	}
        }
        else
        {
        	if (p[pos].loc==0)
        	{
        		pos=(pos+len)%n;
        	}
        	else
        	{
        		pos=(pos+n-len)%n;
        	}
        }
    }
    cout<<p[pos].name;
    return 0;
}

T2 天天爱跑步

Noip近几年最难的题 没有之一,据说sc当年只有6人AC

我到现在还不是很会做,但是大概思路可以理一下

我们先考虑 u ---> LCA(u,v) 这条路径,这是一条向“上”跑的路径。

对与这条路径上的点i来说,当且仅当deep[i]+w[i] = deep[u]时,u节点对i节点是有贡献的。

那么也就是说,只要符合deep[i]+w[i]的全部是玩家起点的点,就能对i点产生贡献。

对于向下走的路径,我们也思考,在什么条件下,这条路径上的点会获得贡献呢?

很明显的,当 dis(u,v)-deep[v] = w[i]-deep[i] 等式成立的时候,这条路径将会对i点有贡献。

所以就可以用树上差分的思想然后就可以开心的贴别人的代码了

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm> 
#define N 300009
#define M 600009
using namespace std;
int en,n,m;
int w[N],spn[M],bucket[N+M],ans[N];
vector<int> v1[M],v2[M],v3[M];
struct nod{
    int u,v,dis,lca;
}p[N];
struct edge{
    int e;
    edge *next;
}*v[N],ed[M];
inline void add_edge(int s,int e){
    en++;
    ed[en].next = v[s],v[s] = ed+en,v[s]->e =e;
}
int read(){
    int x = 0;
    char ch = getchar();
    while(ch < '0' || ch > '9')ch = getchar();
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x;
}
int deep[N],f[N][25],dist[N];
bool use[N];
void dfs(int now,int dep){
    use[now] = true;
    deep[now] = dep;
    for(int k = 1;k <= 22; k++){
        int j = f[now][k-1];
        f[now][k] = f[j][k-1];
    }
    for(edge *e = v[now];e;e=e->next)
      if(!use[e->e]){
           f[e->e][0] = now;
           dist[e->e] = dist[now]+1;
           dfs(e->e,dep+1);
      }
    use[now] = false;
}
inline int jump(int u,int step){
    for(int k = 0; k <= 22; k++)
      if((step & (1<<k)))u = f[u][k];
    return u;
}
inline int qlca(int u,int v){
    if(deep[u] < deep[v])swap(u,v);
    u = jump(u,deep[u]-deep[v]);
    for(int k = 22; k >= 0; k--)
      if(f[u][k] != f[v][k])u = f[u][k],v = f[v][k];
    return u == v ? u : f[u][0];
}
void LCA(){                        //关于LCA的组件
    f[1][0] = 1;
    dfs(1,0);
}
inline void dfs1(int now){
    use[now] = true;
    int prev = bucket[deep[now]+w[now]+N];
    for(edge *e = v[now];e;e=e->next)
        if(!use[e->e])dfs1(e->e);
    bucket[deep[now]+N] += spn[now];
    ans[now] += bucket[deep[now]+w[now]+N]-prev;
    int len = v1[now].size();
    for(int k = 0; k < len;k++)
      --bucket[deep[v1[now][k]]+N];
    use[now] = false;
}
inline void dfs2(int now){
    use[now] = true;
    int prev = bucket[w[now]-deep[now]+N];
    for(edge *e = v[now];e;e=e->next)
      if(!use[e->e])dfs2(e->e);
    int len = v2[now].size();
    for(int k = 0; k < len; k++)
       ++bucket[v2[now][k]+N];
    ans[now] += bucket[w[now]-deep[now]+N] - prev;
    len = v3[now].size();
    for(int k = 0; k < len; k++)
       --bucket[v3[now][k]+N];
    use[now] = false;
}
int main(){
    n = read(),m = read();
    for(int i = 1; i <= n-1; i++){
        int u = read(), v = read();
        add_edge(u,v);
        add_edge(v,u);
    }
    for(int i = 1; i <= n; i++)w[i] = read();
    LCA();                    
    for(int i = 1; i <= m; i++){                //预处理 
        int u = read(),v = read();
        p[i].u = u;
        p[i].v = v;
        p[i].lca = qlca(u,v);
        p[i].dis = dist[u]+dist[v]-dist[p[i].lca]*2;
        spn[u]++;
        v1[p[i].lca].push_back(u);
        v2[v].push_back(p[i].dis-deep[p[i].v]);
        v3[p[i].lca].push_back(p[i].dis-deep[p[i].v]);
    }
    dfs1(1);        //从下至上
    dfs2(1);        //从上至下
    for(int i = 1; i <= m; i++)
       if(deep[p[i].u] == deep[p[i].lca]+w[p[i].lca]) ans[p[i].lca]--;
    for(int i = 1; i <= n; i++)
      printf("%d ",ans[i]);
    printf("\n");
    return 0;
}

T3 水的一批的期望dp

dp[i][j][0/1]表示在第i个时间换了j次课,这次是否成功

那么状态转移方程如下

dp[i][j][0]=min(dp[i-1][j][0]+dis[c[i-1]][c[i]]
	,dp[i-1][j][1]+dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]));
if (j>0)
dp[i][j][1]=min(dp[i-1][j-1][1]+(1-k[i])*(1-k[i-1])*dis[c[i-1]][c[i]]+(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+k[i-1]*k[i]*dis[d[i-1]][d[i]]
         ,dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1-k[i])*dis[c[i-1]][c[i]]);

真的写到眼睛爆炸,而且细节贼多,一不小心就错一片

AC代码


#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>

using namespace std;

int n,m,v,e;

int c[2005],d[2005];

double dp[2005][2005][2];

double ans=1e30;

double k[2005];

int dis[305][305];

int main()
{
	memset(dis,0x3f3f3f,sizeof(dis));
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for (int i=1;i<=v;i++)
       dis[i][i]=0;
    for (int i=1;i<=n;i++)
       scanf("%d",&c[i]);
    for (int i=1;i<=n;i++)
       scanf("%d",&d[i]);
    for (int i=1;i<=n;i++)
       scanf("%lf",&k[i]);
    for (int i=1;i<=e;i++)
    {
    	int x,y,f;
    	scanf("%d%d%d",&x,&y,&f);
    	if (x!=y)
    	   dis[x][y]=dis[y][x]=min(f,dis[x][y]);
    }
    for (int kk=1;kk<=v;kk++)
       for (int i=1;i<=v;i++)
          for (int j=1;j<=v;j++)
             dis[i][j]=min(dis[i][kk]+dis[kk][j],dis[i][j]);
    for (int i=1;i<=n;i++)
       for (int j=0;j<=m;j++)
          dp[i][j][0]=dp[i][j][1]=1e30;
	dp[1][1][1]=0.0;
	dp[1][0][0]=0.0;
	for (int i=2;i<=n;i++)
	   for (int j=0;j<=m;j++)
	   {
	   	   dp[i][j][0]=min(dp[i-1][j][0]+dis[c[i-1]][c[i]]
			               ,dp[i-1][j][1]+dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]));
		   if (j>0)
		   dp[i][j][1]=min(dp[i-1][j-1][1]+(1-k[i])*(1-k[i-1])*dis[c[i-1]][c[i]]+(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+k[i-1]*k[i]*dis[d[i-1]][d[i]]
		                    ,dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1-k[i])*dis[c[i-1]][c[i]]);
	   }
	   
	   for (int i=0;i<=m;i++)
	      ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
	   printf("%0.2lf",ans);
	   return 0;
}

day2

T1感觉很简单但是调了一个小时很难受

直接杨辉三角形求组合数然后去模统计答案就好了

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=2*1e3+5;

int c[maxn][maxn],ans[maxn][maxn];

int cnt[maxn];

int n,m,Mod,t;

void yuchuli()
{
	for (int i=0;i<=maxn-1;i++)
	   c[i][0]=1;
	for (int i=1;i<=maxn-1;i++)
	   for (int j=1;j<=i;j++)
	   {
	   	c[i][j]=(c[i-1][j]%Mod+c[i-1][j-1]%Mod)%Mod;
	   	if (!c[i][j]) ans[i][j]=1;
	   }
	for (int i=1;i<=maxn-1;i++)
	   for (int j=1;j<=i;j++)
	   {
	   	if (i!=j)
	   	ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]+(c[i][j]==0);
	   	else
	   	ans[i][j]=ans[i][j-1]+(c[i][j]==0);
	   }
}

int main()
{
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	scanf("%d%d",&t,&Mod);
	yuchuli();
	while (t--)
	{
		scanf("%d%d",&n,&m);
		printf("%d\n",ans[n][min(n,m)]);
	}
	return 0;
}

T2蚯蚓考场上没想明白,时间全部砸在T3上面了

其实感性的想一想,在你切割之后,放进队列的是同样没有加长的蚯蚓,每次切完之后伸长的量又相同,所以他本身就是单调的,只是答案用一个优先队列去维护就可以了(tail++和++tail天壤之别导致调试一万年)

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

const int maxn=1e7+5;

int cut1[maxn],cut2[maxn],len[maxn];

int n,m,q,u,v,t;

priority_queue<int> ans;

double p; 

bool cmp(int a,int b)
{
	return a>b;
}

int main()
{
    scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
	int head=1,tail=0;
    int head1=1,tail1=0;
    int head2=1,tail2=0;
    for (int i=1;i<=n;i++)
    {
    	scanf("%d",&len[i]);
    	tail++;
    }
    sort(len+1,len+n+1,cmp);
    p=(double)(1.0*u)/(1.0*v);
    for (int i=1;i<=m;i++)
    {
    	int top;
    	if(head>tail)
		{
		    if(cut1[head1]>cut2[head2])
		    	top=cut1[head1++];
			else 
			    top=cut2[head2++];
		}
        else if(len[head]>=cut1[head1]&&len[head]>=cut2[head2])
        	top=len[head++];
        else if(cut1[head1]>=cut2[head2]&&len[head]<=cut1[head1])
		    top=cut1[head1++];
        else top=cut2[head2++];
    	top+=(i-1)*q;
    	int a1=floor(top*1.0*p);
    	int a2=top-a1;
    	a1-=i*q;
    	a2-=i*q;
    	cut1[++tail1]=a1;
    	cut2[++tail2]=a2;
    	if (i%t==0)
    	   printf("%d ",top);
	}
	printf("\n");
    for (int i=head;i<=tail;i++)
       ans.push(len[i]);
    for (int i=head1;i<=tail1;i++)
       ans.push(cut1[i]);
    for (int i=head2;i<=tail2;i++)
       ans.push(cut2[i]);
    for (int i=1;ans.size();i++)
    {
    	if (i%t==0)
    	   printf("%d ",ans.top()+m*q);
    	ans.pop();
    }
    return 0;

又到了激动人心的T3了,是一道经典的状压dp原题

状压主要是用二进制位和子集来表示状态,通常适用于数据比较小的时候(因为他是pow(2,n))

好了来一波dp啊

首先要预处理,这里要涉及精度问题,很开心的是我没有过精度,被卡掉了两组

算了dp方程自己看吧

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const int maxn=20;

const int eps=1e-10;

struct hanshu
{
	double a,b;
	int num;
}f[maxn][maxn];

int dp[1<<maxn];

double x[maxn],y[maxn];

int t,n,m;

void init()
{
	memset(dp,0x3f3f3f,sizeof(dp));
	memset(f,0,sizeof(f));
	memset(x,0,sizeof(x));
	memset(y,0,sizeof(y));
}

void getf(int u,int v)
{
	double x1=x[u];
	double y1=y[u];
	double x2=x[v];
	double y2=y[v];
	double tmp=(y2*x1-y1*x2)/(x1*(x2*x2-x2*x1));
	double b=(y1-tmp*x1*x1)/x1;
	if (tmp-eps>=eps||b<=-eps)
	   return ;
	f[u][v].a=f[v][u].a=tmp;
	f[u][v].b=f[v][u].b=b;
    f[u][v].num=f[v][u].num=f[u][v].num|(1<<(u-1));
	f[u][v].num=f[v][u].num=f[u][v].num|(1<<(v-1));
	return ;
}

void yuli()
{
	for (int i=1;i<=n;i++)
	   for (int j=1;j<=n;j++)
	   {
	   	if (x[i]==x[j])
	   	   continue;
		getf(i,j);
	   }
	for (int i=1;i<=n;i++)
	   for (int j=1;j<=n;j++)
	      for (int k=1;k<=n;k++)
		     if (fabs(f[i][j].a*x[k]*x[k]+f[i][j].b*x[k]-y[k])<=eps&&k!=j&&k!=i)
			    f[i][j].num=f[j][i].num=f[i][j].num|(1<<(k-1)); 
}

int main()
{
	scanf("%d",&t);
	int cmp=t;
	while (t--)
	{
	    scanf("%d%d",&n,&m);
		init();	
		for (int i=1;i<=n;i++)
		   scanf("%lf%lf",&x[i],&y[i]);
		yuli();
		dp[0]=0;
		for (int i=1;i<=n;i++)
			f[i][i].num=(f[i][i].num|(1<<i-1));
		for(int s=0;s<=(1<<n)-1;s++)
		{
			int t=1;
			while(s>>(t-1)&1)
			{
				t+=1;
			}
			dp[s|1<<(t-1)]=std::min(dp[s|(1<<t-1)],dp[s]+1);
			for(int j=t+1;j<=n;j++)
			{
				dp[s|f[t][j].num]=std::min(dp[s|f[t][j].num],dp[s]+1);
			}
		}
		printf("%d\n",dp[(1<<n)-1]);
	}
	return 0;
}

在这里输出的时候有个地方可以注意一下,这里显然不会有n^2条抛物线,所以在状态转移的时候就从找到的第一个没有被打的开始转移,寻找一个子集中有这个点的来进行状态转移,这样就会快很多。比n^2 for 不知道快到哪里去了

好了总结完了,两天下来300+多一点点这就很难受了,所以还要努力啊

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值