求一个排列是第几小(字典序)
用康托展开。
例如 求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)好吧真正错这么多遍的原因是 输!出!格!式!
空格出没的那么诡异真的好吗,好吧,以后做题要把样例复制粘贴观察格式
—————————全剧终——————————————————-