Noip2017赛前的一些记录

前言

已经退役整整五个月了....选考以后终于又摸上了键盘....

但是码力已经大不如前了........

距离比赛也就只有一星期了....那就胡乱的做一些题目吧QAQ

这里是一些根据算法分类的咋杂题摘要

数据结构--强连通分量

城市轰炸【JZOJ5452】

题目描述

战狂也在玩《魔方王国》。他只会征兵而不会建城市,因此他决定对小奇的城市进行轰炸。小奇有\(n\)座城市,城市之间建立了\(m\)条有向的地下通道。战狂会发起若干轮轰炸,每轮可以轰炸任意多个城市。

每座城市里都有战狂部署的间谍,在城市遭遇轰炸时,它们会通过地下通道撤离至其它城市。非常不幸的是,在地道里无法得知其它城市是否被轰炸,如果存在两个不同的城市\(i\)\(j\),它们在同一轮被轰炸,并且可以通过地道从城市\(i\)到达(能通过一些边到达)城市\(j\),那么城市\(i\)的间谍可能因为撤离到城市\(j\)而被炸死。为了避免这一情况,战狂不会在同一轮轰炸城市\(i\)和城市\(j\)

你需要求出战狂最少需要多少轮可以对每座城市都进行至少一次轰炸。

输入格式

第一行两个整数\(n\)\(m\)。接下来\(m\)行每行两个整数\(a\)\(b\)表示一条从\(a\)连向\(b\)的单向边。

输出格式

输出一行仅一个整数表示答案。

分析

这道题目的做法比较显然啊......

显然,强连通块内的点必须一个一个来

显然,如果\(x-->y\)有一条单项边,那么必须通过两次来完成

那么,我们可以先用tarjan缩点,并且那个缩出来的点,然后在DAG上跑最长路径即可,用拓扑跑,时间复杂度\(O(m)\)

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
  
using namespace std;
  
inline char nc(){
    /* 
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
    */return getchar();
}
  
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
  
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c>='a' && c<='z');c=nc()) if (c==EOF) return 0;
    for(;(c>='a' && c<='z');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}
 
inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}
 
int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

struct Edge
{
    int to,nxt;
}edge[1000010];
 
int h[1000010],vis[1000010],sta[1000010],sccc[1000010];
int dfn[1000010],f[1000010];
int ep,cnt,scc,t,n,m;
vector a[1000010];

void addedge(int cu,int cv)
{
    edge[ep].to=cv;
    edge[ep].nxt=h[cu];
    h[cu]=ep++;
}

void Tarjan(int u)
{
    int v;
    dfn[u]=f[u]=++cnt;
    vis[u]=1;
    sta[t++]=u;
    for (int i=h[u];i!=-1;i=edge[i].nxt)
    {
        v=edge[i].to;
        if (!dfn[v])
        {
            Tarjan(v);
            f[u]=min(f[u],f[v]);
        }
        else if (vis[v]) f[u]=min(f[u],dfn[v]);
    }
    if (dfn[u]==f[u])
    {
        ++scc;
        do
        {
            v=sta[--t];
            vis[v]=0;
            sccc[v]=scc;
        }while (u!=v);
    }
}

int main()
{
    read(n);read(m);
    int x,y;
    memset(h,-1,sizeof(h));
    for (int i=1;i<=m;i++)
        read(x),read(y),addedge(x,y);
    memset(dfn,0,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    scc=t=cnt=0;
    int u,v;
    for (u=1;u<=n;u++)
        if (!dfn[u]) Tarjan(u);
    /*for (int i=1;i<=n;i++)
        print(sccc[i]),putchar(' ');
    puts("");print(scc);puts("");*/
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    for (int u=1;u<=n;u++)
    {
        dfn[sccc[u]]++;
        for (int i=h[u];i!=-1;i=edge[i].nxt)
            if (sccc[u]!=sccc[edge[i].to])
                a[sccc[u]].push_back(sccc[edge[i].to]),vis[sccc[edge[i].to]]++;
    }
    queue q;
    memset(f,0,sizeof(f));
    for (int i=1;i<=scc;i++)
        if (vis[i]==0) q.push(i),f[i]=dfn[i];
    while (!q.empty())
    {
        x=q.front();q.pop();
        for (int i=0;i

数据结构--树状数组

斯诺(snow)

题目描述

“谨向英勇的中国致敬” 埃德加·斯诺到达陕甘宁边区后,决定出个 NOIP(NOI Professional)题,向西方世界介绍他所看到的事情. 斯诺在调查中发现,Mao Zedong 先生非常喜欢序列和区间,于是斯诺给了 Mao Zedong 先生一个长度 为 n 的,每个元素都是 0,1,2 的序列. Mao Zedong 发现这个序列中一共有\(n(n+1)/2\)个区间,这太多了.运用辩证法,他认为,对于一个区间,只 有其中任意一种数字的数目都不超过区间的一半时,这个区间才算是革命的.现在 Mao Zedong 先生建议 斯诺数一数这个序列里有多少个革命的区间. 例如,对于序列 010102201,区间”201”是合法的,里面每种数字只占三分之一.区间”2”是不革命的,2 占 据了这个区间的 100%.(由此可以看出,长度为 1 的区间一定不合法).区间”0101”和“10”是合法的,0 和 1 各 占据了区间的一半,但是没有超过一半.

输入格式

第一行一个整数 n,表示区间的长度.

第二行一个长度为 n 的只包含 0,1,2 的字符串,表示给出的序列.

输出格式

一行一个整数,表示革命的区间的数量

数据范围

第 1 个测试点,\(n=100\)

第 2,3 个测试点,\(n=1000\)

第 4 个测试点,\(n=50000\)

第 5,6 个测试点,\(n=10^5\)

第 7,8,9,10 个测试点,\(n=5*10^6\)

第 2,4,5,7 个测试点还满足:给出的序列中只含0和1

分析

前三个点,前缀和预处理,直接暴力

对于0\1的情况,显然必须要0/1相等相等的情况才符合,用1/-1分别表示0/1,即求前缀和相等的数对,暴力即可

这样就能获得60分了.....

这时候,我想着倒过来思考,很容易发现,倒着思考的话,一个区间超过一半的数是唯一的。换句话说,我们可以分别从总方案\(\frac{n*(n+1)}{2}\)中分别减去\(0\)\(1\)\(2\)超过一半的情况,这样保证是不重复的

对于同一种数字带来的不合法区间数,我们可以把这种数字看作\(-1\),其他数字看作\(1\),统计和为负数的区间个数

我们可以利用前缀和转换成求逆序对个数,树状数组都可以在\(O(nlogn)\)的时间内得到结果

但显然这道题目需要的是线性复杂度的做法,带\(log\)的卡不过去

我们这个时候发现,对于每一次树状数组的询问,对于上一次询问\(query(x)\),我们要么询问\(query(x-1)\),要么询问\(query(x+1)\)

所以我们不需要询问区间,可以改成桶状存贮,然后直接O(1)转移

下面的代码给出了各个部分的做法,最后一部分else中的为满分做法

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c>='0' && c<='2');c=nc()) if (c==EOF) return 0;
    for(;(c>='0' && c<='2');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}

inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m;
char ch[5000010];
int f1[5000010],f2[5000010],f0[5000010];
LL f[10000010],c[10000010];

bool check()
{
    for (int i=1;i<=n;i++)
        if (ch[i]=='2') return false;
    return true;
}

void change(int x,LL y)
{
    for (;x<=2*n+1;x+=x&(-x)) c[x]+=y;
}

LL query(int x)
{
    LL res=0;
    for (;x;x-=x&(-x)) res+=c[x];
    return res;
}

LL solve()
{
    LL res=0;
    memset(c,0,sizeof(c));
    for (int i=0;i<=n;i++)
        res+=query(2*n)-query(f1[i]),
        change(f1[i],1);
    return res;
}

LL Solve()
{
    LL res=0,s=0;
    memset(c,0,sizeof(c));
    for (int i=0;i<=n;i++)
    {
        res+=i-s,c[f1[i]]++;
        s++;
        if (f1[i+1]>f1[i]) s+=c[f1[i+1]];else s-=c[f1[i]];
    }
    return res;
}

int main()
{
    freopen("snow.in","r",stdin);
    freopen("snow.out","w",stdout);
    read(n);
    m=read(ch);
    if (n<=3000)
    {
        f1[0]=0;f2[0]=0;f0[0]=0;
        for (int i=1;i<=n;i++)
        {
            f0[i]=f0[i-1],f1[i]=f1[i-1],f2[i]=f2[i-1];
            if (ch[i]=='0') f0[i]++;
            if (ch[i]=='1') f1[i]++;
            if (ch[i]=='2') f2[i]++;
        }
        int ans=0;
        for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
            if (f0[j]-f0[i-1]<=(j-i+1)/2&&f1[j]-f1[i-1]<=(j-i+1)/2&&f2[j]-f2[i-1]<=(j-i+1)/2) ans++;
        print(ans),puts("");
    }
    else if (check())
    {
        f0[0]=0;
        memset(f,0,sizeof(f));f[n]=1;
        for (int i=1;i<=n;i++)
        {
            if (ch[i]=='0') f0[i]=f0[i-1]+1;else f0[i]=f0[i-1]-1;
            f[f0[i]+n]++;
        }
        LL ans=0LL;
        for (int i=0;i<=n+n;i++)
            ans+=f[i]*(f[i]-1LL)/2LL;
        print(ans),puts("");
    }
    else if (n<=100000)
    {
        for (int i=1;i<=n;i++)
            if (ch[i]=='0') f0[i]=-1;else f0[i]=1;
        f1[0]=0;
        for (int i=1;i<=n;i++)
            f1[i]=f1[i-1]+f0[i];
        for (int i=0;i<=n;i++)
            f1[i]+=n+1;
        LL ans=((LL)n)*((LL)(n+1))/2LL;
        ans-=solve();
        for (int i=1;i<=n;i++)
            if (ch[i]=='1') f0[i]=-1;else f0[i]=1;
        f1[0]=0;
        for (int i=1;i<=n;i++)
            f1[i]=f1[i-1]+f0[i];
        for (int i=0;i<=n;i++)
            f1[i]+=n+1;
        ans-=solve();
        for (int i=1;i<=n;i++)
            if (ch[i]=='2') f0[i]=-1;else f0[i]=1;
        f1[0]=0;
        for (int i=1;i<=n;i++)
            f1[i]=f1[i-1]+f0[i];
        for (int i=0;i<=n;i++)
            f1[i]+=n+1;
        ans-=solve();
        print(ans),puts("");
    }
    else
    {
        for (int i=1;i<=n;i++)
            if (ch[i]=='0') f0[i]=-1;else f0[i]=1;
        f1[0]=0;
        for (int i=1;i<=n;i++)
            f1[i]=f1[i-1]+f0[i];
        for (int i=0;i<=n;i++)
            f1[i]+=n+1;
        LL ans=((LL)n)*((LL)(n+1))/2LL;
        ans-=Solve();
        for (int i=1;i<=n;i++)
            if (ch[i]=='1') f0[i]=-1;else f0[i]=1;
        f1[0]=0;
        for (int i=1;i<=n;i++)
            f1[i]=f1[i-1]+f0[i];
        for (int i=0;i<=n;i++)
            f1[i]+=n+1;
        ans-=Solve();
        for (int i=1;i<=n;i++)
            if (ch[i]=='2') f0[i]=-1;else f0[i]=1;
        f1[0]=0;
        for (int i=1;i<=n;i++)
            f1[i]=f1[i-1]+f0[i];
        for (int i=0;i<=n;i++)
            f1[i]+=n+1;
        ans-=Solve();
        print(ans),puts("");
    }
    return 0;
}

图论--K短路

牛跑步【Usaco2008Mar】

题目描述

Bessie has taken heed of the evils of sloth and has decided to get fit by jogging from the barn to the pond several times a week. She doesn't want to work too hard, of course, so she only plans to jog downhill to the pond and then amble back to the barn at her leisure.

Bessie also doesn't want to jog any too far either, so she generally takes the shortest sequence of cow paths to get to the pond. Each of the M (1 <= M <= 10,000) cow paths connects two pastures

conveniently numbered 1..N (1 <= N <= 1000). Even more conveniently, the pastures are numbered such that if X>Y then the cow path from pasture X to pasture Y runs downhill. Pasture N is the barn (at the top of the hill) and pasture 1 is the pond (at the bottom).

Just a week into her regimen, Bessie has begun to tire of always taking the same route to get to the pond. She would like to vary her route by taking different cow paths on different days. Specifically, Bessie would like to take exactly K (1 <= K <= 100) different routes for variety. To avoid too much exertion, she wants these to be the K shortest routes from the barn to the pond. Two routes are considered different if they comprise different sequences of cow paths.

Help Bessie determine how strenuous her workout will be by determining the lengths of each of the K shortest routes on the network of pastures. You will be supplied a list of downhill cow paths from X_i to Y_i along with the cow path's length: (X_i, Y_i, D_i) where (1 <= Y_i < X_i; Y_i < X_i <= N). Cowpath i has length D_i (1 <= D_i <= 1,000,000).

输入格式

Line 1: Three space-separated integers: N, M, and K

Lines 2..M+1: Line i+1 describes a downhill cow path using three space-separated integers: X_i, Y_i, and D_i

输出格式

Lines 1..K: Line i contains the length of the i-th shortest route or -1 if no such route exists. If a shortest route length occurs multiple times, be sure to list it multiple times in the output.

分析

K短路的模板题,就当做复习,又写了一遍代码

分析来源:hzwer

1.A*算法

A*算法在人工智能中是一种典型的启发式搜索算法,启发中的估价是用估价函数表示的:\[h(n)=f(n)+g(n)\]

其中\(f(n)\)是节点n的估价函数,g(n)表示实际状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。另外定义h'(n)为n到目标节点最佳路径的实际值。如果h'(n)≥h(n)则如果存在从初始状态走到目标状态的最小代价的解,那么用该估价函数搜索的算法就叫A*算法。

2 第K最短路的算法

我们设源点为s,终点为t,我们设状态f(i)的g(i)为从s走到节点i的实际距离,h(i)为从节点i到t的最短距离,从而满足A*算法的要求,当第K次走到f(n-1)时表示此时的g(n-1)为第K最短路长度。

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#include 
#define LL long long
 
using namespace std;
 
inline char nc(){
    /* 
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
    */return getchar();
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c>='a' && c<='z');c=nc()) if (c==EOF) return 0;
    for(;(c>='a' && c<='z');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}

inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}
int n,m,k,s,t;
int tot,point[10010],nxt[10010],v[10010],_tot,_point[10010],_nxt[10010],_v[10010];LL c[10010],_c[10010];
LL dis[10010],ans[10010];bool vis[10010];int cnt[10010];
struct data
{
    int x;LL g,h;
    bool operator < (const data &a) const
    {
        return g+h>a.g+a.h;
    }
};

void add(int x,int y,LL z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    ++_tot; _nxt[_tot]=_point[y]; _point[y]=_tot; _v[_tot]=x; _c[_tot]=z;
}
void spfa()
{
    queue  q;
    memset(dis,127,sizeof(dis));
    dis[t]=0;vis[t]=1;q.push(t);
    while (!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for (int i=_point[now];i;i=_nxt[i])
            if (dis[_v[i]]>dis[now]+_c[i])
            {
                dis[_v[i]]=dis[now]+_c[i];
                if (!vis[_v[i]]) vis[_v[i]]=1,q.push(_v[i]);
            }
    }
}

void Astar()
{
    priority_queue  q;
    q.push((data){s,0,dis[s]});
    int size=0;
    while (!q.empty())
    {
        data now=q.top();q.pop();
        ++cnt[now.x];
        if (cnt[now.x]>k) continue;
        if (now.x==t) ans[++size]=now.g;
        if (cnt[t]==k) return;
        for (int i=point[now.x];i;i=nxt[i])
            q.push((data){v[i],now.g+c[i],dis[v[i]]});
    }
}

int main()
{
    read(n),read(m),read(k);
    int x,y;LL z;
    for (int i=1;i<=m;++i)
    {
        read(x),read(y),read(z);
        if (x

图论--二分图匹配

感觉已经完全把这玩意的建图和处理忘记了,那就暂且抱着联赛不考网络流的心态,看个匈牙利把QAQ

超级英雄Hero【HNOI2006】

题目描述

现在电视台有一种节目叫做超级英雄,大概的流程就是每位选手到台上回答主持人的几个问题,然后根据回答问题的多少获得不同数目的奖品或奖金。主持人问题准备了若干道题目,只有当选手正确回答一道题后,才能进入下一题,否则就被淘汰。为了增加节目的趣味性并适当降低难度,主持人总提供给选手几个“锦囊妙计”,比如求助现场观众,或者去掉若干个错误答案(选择题)等等。 这里,我们把规则稍微改变一下。假设主持人总共有m道题,选手有n种不同的“锦囊妙计”。主持人规定,每道题都可以从两种“锦囊妙计”中选择一种,而每种“锦囊妙计”只能用一次。我们又假设一道题使用了它允许的锦囊妙计后,就一定能正确回答,顺利进入下一题。现在我来到了节目现场,可是我实在是太笨了,以至于一道题也不会做,每道题只好借助使用“锦囊妙计”来通过。如果我事先就知道了每道题能够使用哪两种“锦囊妙计”,那么你能告诉我怎样选择才能通过最多的题数吗?

输入格式

输入文件的一行是两个正整数n和m(0 < n <1001,0 < m < 1001)表示总共有n中“锦囊妙计”,编号伟0~n-1,总共有m哥问题。

以下的m行,每行两个数,分别表示第m个问题可以使用的“锦囊妙计”的编号。

注意,每种编号的“锦囊妙计”只能使用一次,同一个问题的两个“锦囊妙计”可能一样。

输出格式

第一行为最多能通过的题数p。

分析

显然,这肯定是一个二分图,题目为一部分点,锦囊为另一部分点

对于每一个题目,向两个锦囊连边,然后跑一个最大匹配就可以了

不过也需要注意,他不是完全的最大匹配,他的题目是依次的,所以当一个匹配失败以后就不能在继续匹配下去了

记得以前二分图匹配都是写连接矩阵的......那就写个领接表的玩玩QAQ

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}

int lk[1001];
int n,m;
bool y[1001];
vector mp[1001];

bool find(int x)
{
    for (int i=0;i

动态规划

保镖排队

题目描述

CZ一共雇佣了\(N\)个保镖(编号为\(1\)\(N\))。每个保镖虽然身手敏捷武功高强,但是他在其余\(N-1\)个保镖里,都会有一个“上司”,他会对他的上司言听计从。但一号保镖例外,他武功盖世,不惧怕其余任何保镖,所以他没有上司。

CZ会对这 N 个保镖进行定期视察。每次视察的时候,首先会让所有保镖排队。 对于每个保镖,在他心目中会对他的所有下属的武功实力排个队。现在 CZ 要求排出来的队伍满足:

①互为上司-下属的两个保镖,上司在前,下属在后

②对于一个保镖的所有下属,武功实力较强的在前,较弱的在后。

CZ 想知道,总的排队方法数除以\(10007\)的余数是多少。

输入格式

输入的第一行为一个正整数 T,表示了数据组数。

对于每组数据:

第一行为一个正整数 N。

接下来 N 行,每行描述一个保镖。

第 i+1 行,会有一个整数 K,代表第 i 个保镖的下属个数,接下来 K 个数,代表第 i 个保镖的下属按照武功实力从高到低的编号。

输出格式

输出包括 C 行,每行对于每组数据输出方案数 mod 10007 后的结果。

分析

很基础的一道树形dp

我们可以观察到,对于一个节点,加入他的儿子已经按照题意排成了合法的序列,那么我们可以把比这个节点级别小的兄弟节点(同一个父亲)按照他们应该的顺序,随意的插到除他本身外的所有位置

即,把比他级别小的所有节点和他们的孩子,任意的插到他的孩子当中,假设\(c[x]\)\(x\)节点为根节点的孩子数量,\(cn[x]\)为比\(x\)级别小的所有兄弟节点和他们的孩子的数量,\(f[x]\)\(x\)节点为根节点的方案数

这样推出的转移方程是:\(f[x]=f[x]*G^{cn[x]}_{c[x]}\),其中\(G^{cn[x]}_{c[x]}\)表示\(cn[x]\)个形同的球放入\(c[x]\)个不同抽屉的方案数(如何等价过来的,可以自己yy一下),不了解这个求法的可以参考下文数学中的“小球问题的万能图”

当然,还有一个很显然的注意不能忘记\(f[x]=\prod f[y](y\epsilon child[x]))\)

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c>='a' && c<='z');c=nc()) if (c==EOF) return 0;
    for(;(c>='a' && c<='z');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}

inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int T,n,m;
LL fac[2010],ifac[2010];
const LL mo=10007;
const int N=2000;
vector a[1010];
int f[1010],c[1010],ne[1010],cn[1010];

LL pow(LL x,LL y)
{
    LL res=1;
    for (;y;y>>=1)
    {
        if (y&1) res=res*x%mo;
        x=x*x%mo;
    }
    return res;
}

LL C(int x,int y)
{
    if (y==0 || x==y) return 1LL;
    if (x=1;i--) 
        ifac[i]=ifac[i+1]*(i+1)%mo;
}

void dfs(int x)
{
    int s=0;
    for (int i=0;i=0;i--)
    {
        solve(a[x][i]);
        f[x]=f[x]*f[a[x][i]]%mo;
        if (i!=a[x].size()-1) f[x]=f[x]*C(c[a[x][i]]-1+cn[a[x][i]],c[a[x][i]]-1)%mo;
    }
}

int main()
{
    read(T);init();
    while (T--)
    {
        read(n);
        int x,y;
        memset(ne,0,sizeof(ne));
        memset(f,0,sizeof(f));
        memset(c,0,sizeof(c));
        memset(cn,0,sizeof(cn));
        for (int i=1;i<=n;i++)
        {
            a[i].clear();
            read(x);
            while (x--) read(y),a[i].push_back(y);
        }
        dfs(1);
        solve(1);
        print(f[1]);puts("");
    }
    return 0;
}

数学

小球问题的万能图

(PS.我没记错的话,这张图片是cwy给我的...不过有个错误,我已经更正了QwQ)

Noip2017赛前的一些记录_第1张图片

Biscuits(AtCoder Grand Contest 017)

题目描述

There are N bags of biscuits. The i-th bag contains Ai biscuits.

Takaki will select some of these bags and eat all of the biscuits inside. Here, it is also possible to select all or none of the bags.

He would like to select bags so that the total number of biscuits inside is congruent to P modulo 2. How many such ways to select bags there are?

输入格式

Input is given from Standard Input in the following format:

N P
A1 A2 ... AN

输出格式

Print the number of ways to select bags so that the total number of biscuits inside is congruent to P modulo 2.

数据范围

1≤N≤50
P=0 or 1
1≤Ai≤100

分析

我们可以发现,偶数对答案是没有贡献的....

可以发现,奇数的情况就是有奇数个奇数,偶数的情况就是有偶数个奇数

很显然的,这两个答案是相同的,可以写一下组合式,很容易发现

那么,不管奇数还是偶数,答案就是总答案的一半,即\(\frac{2^n}{2}\)

有一个特殊的情况,当奇数个数为0的时候,显然,奇数为0,而偶数答案就是\(2^n\)

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
  
using namespace std;
  
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
  
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
  
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
int wt,ss[19];
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,b[4];

LL pow(LL x,int y)
{
    LL res=1;
    for (;y;y>>=1)
    {
        if (y&1) res*=x;
        x*=x;
    }
    return res;
}

int main()
{
    read(n);read(m);
    int x;
    for (int i=1;i<=n;i++)
        read(x),b[x%2]++;
    if (b[1]==0)
    {
        if (m==0) print(pow(2LL,b[0]));
        else print(0);
    }
    else print(pow(2LL,b[0]+b[1])/2);
    puts("");
    return 0;
}

Moderate Differences(AtCoder Grand Contest 017)

题目描述

There are N squares in a row. The leftmost square contains the integer A, and the rightmost contains the integer B. The other squares are empty.

Aohashi would like to fill the empty squares with integers so that the following condition is satisfied:

For any two adjacent squares, the (absolute) difference of the two integers in those squares is between C and D (inclusive).
As long as the condition is satisfied, it is allowed to use arbitrarily large or small integers to fill the squares. Determine whether it is possible to fill the squares under the condition.

输入格式

Input is given from Standard Input in the following format:

N A B C D

输出格式

Print YES if it is possible to fill the squares under the condition; print NO otherwise.

数据范围

3≤N≤500000
0≤A≤109
0≤B≤109
0≤C≤D≤109
All input values are integers.

分析

我们可以做一个差分,\(y_i=y_i-y_{i+1}\),那么,显然\(\sum y_i=B-A\),而所有的\(y_i\)需要满足\(-C\leq y_i\leq -D\)或者\(C\leq y_i\leq D\)

假设有\(M\)个处于范围\(-C\leq y_i\leq -D\),则剩下的\(N-1-M\)个位于\(C\leq y_i\leq D\)

这样,我们直接\(O(N)\)枚举\(M\)的大小,然后可以通过\(O(1)\),由上一次的答案的得到

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
  
using namespace std;
  
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
  
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

LL N,A,B,C,D,Min,Max;

bool check(LL x)
{
    if (Min<=x&&Max>=x) return true;
    for (LL i=1;i<=(N-1);i++)
    {
        Min=Min-C-D;Max=Max-C-D;
        if (Min<=x&&Max>=x) return true;
    }
    return false;
}

int main()
{
    read(N);read(A);read(B);read(C);read(D);
    LL x=B-A;Min=C*(N-1);Max=D*(N-1);
    if (check(B-A)) puts("YES");else puts("NO");
    return 0;
}

数列递推(LibreOJ NOIP Round #1 Day 1)

题目描述

sosusosu虐爆OI之后成为了一名文化课选手。一天,他做作业碰到了一堆数列问题,每道题给出的数列都是以下形式:

给定一个下标从\(0\)开始,无限长的整数列\(a_i\),\(\ i\in \mathbb{N}\),已知\(a_0\),\(a_1\)的值,以及递推式\(a_{i+2}=k*a_{i+1}+a_i,\ i\in\mathbb{N},k\in {\mathbb{N}^+}\)

sosusosu 研究了这些数列,发现它们十分优美充满人类智慧,于是决定出一道OI题。

sosusosu 给了你一个集合\(S\subset \mathbb{N}\),他想问你对于\(S\)中的每个数\(s_i\),使得\(a_{s_i}\)最大的\(s_i\)和使得\(a_{s_i}\)最小的\(s_i\)分别是多少。如果这样的\(s_i\)有多个,请你回答最小的一个。

另外,sosusosu 准备对他作业中碰到的每个数列都让你回答一次,不过每次的集合\(S\)是一样的。

输入格式

输入第一行一个整数\(m\)表示\(S\)中元素个数。

第二行\(m\)个整数\(s_1,s_2,\cdots ,s_m\)表示\(S\)中的元素。保证它们是非负整数且严格递增(即\(s_i)。

第三行一个整数\(n\)表示询问的数列个数。

接下来\(n\)行每行三个整数\(a_0, a_1, k\)描述一个数列。

输出格式

输出共\(n\)行,每行依次输出两个整数 \(maxsi,maxsi\),依次表示\(S\)的元素\(s_i\)中,使得\(a_{s_i}\)最大的\(s_i\)和使得\(a_{s_i}\)最小的\(s_i\)的值。如果这样的\(s_i\)有多个,请你回答最小的一个。

分析

这其实是一道纯真·暴力啊?

很简单的试着写暴力以后就能发现,到一定程度以后数列就会呈现出单调的趋势

更准确的说,当连续两个数的符号相同以后,数列就一定单调了

官网题解上有严格证明说,这样的情况,数列项数不会超过36项

Noip2017赛前的一些记录_第2张图片

这样的话,直接暴力前几项,然后对集合排个序就可以高辣......

感觉我的常数写的还是不错的.....

Noip2017赛前的一些记录_第3张图片

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#define LL long long
  
using namespace std;
  
inline char nc(){
    ///*
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
    //*/return getchar();
}
  
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
  
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c>='0' && c<='9');c=nc()) if (c==EOF) return 0;
    for(;(c>='0' && c<='9');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}
 
inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}
 
int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,a[300010];
LL f[100];

bool cmp(int x,int y)
{
    return xf[0]||f[i]>f[1])
                i++,f[i]=z*f[i-1]+f[i-2];
            maxm=f[a[1]],minm=f[a[1]],maxw=a[1],minw=a[1];
            int j=2;a[n+1]=i+2;
            while (a[j]<=i)
            {
                if (f[a[j]]>maxm) maxm=f[a[j]],maxw=a[j];
                if (f[a[j]]i) minw=a[n];
        }
        else
        {
            i--;
            while (f[i]maxm) maxm=f[a[j]],maxw=a[j];
                if (f[a[j]]i) maxw=a[n];
        }
        print(maxw),putchar(' '),print(minw),puts("");
    }
    return 0;
}

搜索--BFS

小X的密室

题目描述

小 X 正困在一个密室里,他希望尽快逃出密室。

密室中有 N 个房间,初始时,小 X 在 1 号房间,而出口在 N 号房间。

密室的每一个房间中可能有着一些钥匙和一些传送门,一个传送门会单向地创造一条从房间 X 到房间 Y 的通道。另外,想要通过某个传送门,就必须具备一些种类的钥匙(每种钥匙都要有才能通过)。幸运的是,钥匙在打开传送门的封印后,并不会消失。

然而,通过密室的传送门需要耗费大量的时间,因此,小 X 希望通过尽可能少的传送门到达出口,你能告诉小 X 这个数值吗?

另外,小 X 有可能不能逃出这个密室,如果是这样,请输出 "No Solution"。

输入格式

第一行三个整数 N,M,K,分别表示房间的数量、传送门的数量以及钥匙的种类数。

接下来 N 行,每行 K 个 0 或 1,若第 i 个数为 1,则表示该房间内有第 i种钥匙,若第 i 个数为 0,则表示该房间内没有第 i种钥匙。

接下来 M 行,每行先读入两个整数 X,Y,表示该传送门是建立在 X号房间,通向 Y 号房间的,再读入 K 个 0 或 1,若第 i 个数为 1,则表示通过该传送门需要 i 种钥匙,若第 i 个数为 0,则表示通过该传送门不需要第 iii 种钥匙。

输出格式

输出一行一个 "No Solution",或一个整数,表示最少通过的传送门数。

分析

我们用\(0/1\)表示这把钥匙的又或者没有,直接用一个十进制的状态来记录(即状态压缩)

\(f[i][j]\)表示到\(i\)号房间,钥匙的状态为\(j\)的最少步数

由于拓扑序不知,但是边的程度均为\(1\),所以直接BFS即可

#include  
#include  
#include  
#include  
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
  
using namespace std;
  
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
  
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
  
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c>='a' && c<='z');c=nc()) if (c==EOF) return 0;
    for(;(c>='a' && c<='z');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}
 
inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}
 
int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,k,a[5010],f[5010][2010];
struct data
{
    int x,a;
    data(int A=0,int B=0):x(A),a(B){}
};
vector b[5010];
struct dp
{
    int x,a,c;
    dp(int A=0,int B=0,int C=0):x(A),a(B),c(C){}
};
queue q;
bool v[5010][2010];

int main()
{
    freopen("room.in","r",stdin);
    freopen("room.out","w",stdout);
    read(n);read(m);read(k);
    int x,y,z,u;
    for (int i=1;i<=n;i++)
    {
        x=0;
        for (int j=1;j<=k;j++)
            read(y),x=x*2+y;
        a[i]=x;
    }
    for (int i=1;i<=m;i++)
    {
        read(x),read(y);z=0;
        for (int j=1;j<=k;j++)
            read(u),z=z*2+u;
        b[x].push_back(data(y,z));
    }
    for (int i=1;i<=n;i++)
        for (int j=0;j<=1050;j++)
            f[i][j]=m*1000,v[i][j]=false;
    q.push(dp(1,a[1],0));
    f[1][a[1]]=0;v[1][a[1]]=true;
    while (!q.empty())
    {
        x=q.front().x;y=q.front().a;z=q.front().c;q.pop();
        for(int i=0;i

转载于:https://www.cnblogs.com/xiejiadong/p/7800752.html

你可能感兴趣的:(数据结构与算法,人工智能)