洛谷9月noip模拟赛

第一题:

求一个排列是第几小(字典序)
用康托展开。
例如 求3214的字典序。
我们从高位开始比较,比3小的有两位,即2*3!=12,再比较下一位2,比2小的只有1,且1没有用过,所以再加1*2!=2,再比较1,没有比1小的数,最后一位因为比它小的树必定已经全部被用过,所以不需要比较。
故3214的字典序为12+2+1=14.
所以对于这道题,我们先预算出1~10的阶乘,然后再康托展开即可。
贴代码:

#include
#include
#include
#include
using namespace std;
int n,ans,num;
string s;
int a[11];
bool vis[11];
int main()
{
    scanf("%d",&n);
    cin>>s;
    a[0]=1;
    for(int i=1;i<=10;i++)
     a[i]=a[i-1]*i;
    memset(vis,true,sizeof(vis));
    ans=1;
    for(int i=0;iint k=s[i]-'0';
        vis[k]=false;
        num=0;
        for(int j=1;jif(vis[j]==true)num++;
        ans+=num*a[n-i-1];
    }
    cout<

第二题:

求树上任意两点之间所有边权的异或。
设a,b两点的最近公共祖先为c,题目即为求a~c~b的异或,可以知道x^y^y=x,故只需求a,b到树根的异或的异或,从树根到c的边权异或两次后相当于未运算。
贴代码:

#include
#include
#include
#include
using namespace std;
struct Node{
    int to,val,next; 
}e[200005];
int head[100005];
bool vis[100005];
int xo[100005];
int tot=0;
void add(int u,int v,int w)
{
    e[++tot].to=v;
    e[tot].val=w;
    e[tot].next=head[u];
    head[u]=tot;
}
void dfs(int u)
{
    vis[u]=true;
    for(int i=head[u];i!=0;i=e[i].next)
      if(!vis[e[i].to])
      {
         int v=e[i].to;
         xo[v]=xo[u]^e[i].val;
         dfs(v); 
      }
}

int main()
{
    int u,v,w,n,m;
    scanf("%d",&n);
    memset(head,0,sizeof(head));
    for(int i=1;iscanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w); 
    }
    xo[u]=0;
    dfs(1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        printf("%d\n",xo[u]^xo[v]);
    }
    return 0;
}

第三题:

题目链接:http://www.luogu.org/problem/show?pid=2583
这次考试唯一有价值的题目。
看完题目后,第一感觉是动规,然而自己想了想状态转移方程并没有想出来——>捂脸(其实是状态选择的不太对,造成转移困难)。
于是就去问学长了,学长说是最短路,(i,j)表示第i时刻在第j站,如果原地等待就在(i,j)到(i+1,j)建边
然而接受了学长的教导后,我并没有做出来23333
Golve最后提供了动规的思路%%%
Dp[i][j]表示i时刻,在第j站到达n最少还需等待多长时间。
初始化:dp[t][n]=0;
Dp[t][1~n-1]=INF.
我们还需要一个数组hastrain[i][j][0]表示i时刻j车站有一辆从左到右的火车;
Hastrain[i][j][1]表示i时刻j车站有一辆从右到左的火车。
Hastrain可以在读入时就处理好。
对于题目中的每个点,我们一定是从后更新前,因为最后的状态是确定的(初始化),所以每个点会有三种情况。
1.原地等待 dp[i][j]=dp[i+1][j]+1;
2.从左开向右
满足条件 j< n,hastrain[i][j][0]==true,i+ti[j]<=t
Dp[i][j]=min(dp[i][j],dp[i+ti[j]][j+1]);
3.从右向左开
满足条件j>1,hastrain[i][j][1]==true,i+ti[j-1]<=t
Dp[i][j]=min(dp[i][j],dp[i+ti[j-1]][j-1]);
输出按照格式就可以AC啦。
贴代码:

#include
#include
#include
#include
using namespace std;
int n,t,m,INF;
int ti[60];
bool hastrain[300][60][3];
int dp[300][53];
int main()
{
    int ca=0;
    while(scanf("%d",&n)&&n!=0)
    {
       ca++;
       scanf("%d",&t);
       INF=t;
       ti[0]=0;
       for(int i=1;iscanf("%d",&ti[i]),INF-=ti[i];
       INF+=5;
       memset(hastrain,false,sizeof(hastrain));
       int x;
       scanf("%d",&m);
       for(int i=1;i<=m;i++)
       {
          scanf("%d",&x);
          hastrain[x][1][0]=true;
          int num=x;
          for(int j=1;j<=n;j++)
          {
             num+=ti[j];
             if(num>t)break;
             hastrain[num][j+1][0]=true;
          }
       }
       scanf("%d",&m);
       for(int i=1;i<=m;i++)
       {
          scanf("%d",&x);
          hastrain[x][n][1]=true;
          int num=x;
          for(int j=n-1;j>=1;j--)
          {
             num+=ti[j];
             if(num>t)break;
             hastrain[num][j][1]=true;
          }
       }
       for(int i=1;i<=n-1;i++)dp[t][i]=INF;
       dp[t][n]=0;
       for(int i=t-1;i>=0;i--)
         for(int j=1;j<=n;j++)
         {
            dp[i][j]=dp[i+1][j]+1;

            if(j0]&&i+ti[j]<=t)
             dp[i][j]=min(dp[i][j],dp[i+ti[j]][j+1]);

            if(j>1&&hastrain[i][j][1]&&i+ti[j-1]<=t)
             dp[i][j]=min(dp[i][j],dp[i+ti[j-1]][j-1]);
         } 
      cout<<"Case Number "<": ";
      if(dp[0][1]>=INF)cout<<"impossible\n";
      else cout<0][1]<<"\n";
    }
    return 0;
}

总结:前两道题都是一次通过的,然后最后一道过了5~6遍,得了85.74。
让我们总结一下为什么错这么多遍吧。(错误不分先后,记忆废)
(1)貌似一开始INF设错了,最大应该是总时间t减去通往每个站的时间,但我一开始设为了一个大整数。
(2)然后提交后还是错误,我心想不可能吧,是不是服务器出错了,然后什么都没改又交了一遍(真是傻到无可救药了)
(3)好吧真正错这么多遍的原因是 输!出!格!式!
空格出没的那么诡异真的好吗,好吧,以后做题要把样例复制粘贴观察格式
—————————全剧终——————————————————-

你可能感兴趣的:(洛谷9月noip模拟赛)