NOI 2011

 

[NOI2011]兔农

 暴力O(n)递推就有75。。。记下模k的结果和模g的结果,遇到模k等于1两个都减一。

注意滚动。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e7+10;
long long f[5],g[5];
long long n,k,mod; 

int main()
{
    scanf("%lld%lld%lld",&n,&k,&mod);
    f[1]=f[2]=1;
    g[1]=g[2]=1;
    for(long long i=3;i<=n;i++)
    {
        f[3]=(f[1]+f[2])%k;
        g[3]=(g[1]+g[2])%mod;
        if(f[3]%k==1) g[3]--,f[3]=0;
        g[3]=(g[3]+mod)%mod;
        g[1]=g[2],g[2]=g[3];
        f[1]=f[2],f[2]=f[3];
    }
    if(n<3) g[3]=g[1];
    printf("%lld",g[3]%mod);
    return 0;
}
View Code

 

[NOI2011]智能车比赛

很精彩的一道题。

可以发现所有的拐点都在矩形的四个角,有DP:

这个DP用spfa来转移,只不过在扩展队首的时候,要判断是否能够到达。

现在就变成了如何在最多O(n)时间内判断两点是否可以直接互相达到。

对于每个点,扩展的时候,维护一个high,一个low,表示张角的限制,因为已经按照x排过序了,所以通过叉积的计算就可以更新啦。

注意high只能用矩形的2个上角更新,low同理。

判断的时候也用叉积。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=9003;
struct Line
{
    double x,y;
}line[maxn],s,t;
int n,st,vis[maxn];
double v,dis[maxn];

double DIS(Line a,Line b) 
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double cross(Line o,Line a,Line b) 
{
    double x1=a.x-o.x,y1=a.y-o.y;
    double x2=b.x-o.x,y2=b.y-o.y;
    return x1*y2-x2*y1;
}

bool check(int up,int low,Line a,Line b) 
{
    return !((up && cross(a,line[up],b)>0) || (low && cross(a,line[low],b)<0));
}


inline void spfa()
{
    queue<int> q;
    for(int i=st;i<=n;i++) dis[i]=1e9;
    dis[st]=0;
    vis[st]=1;
    q.push(st);
    while(!q.empty())
    {
        int tt=q.front();
        q.pop();
        vis[tt]=0;
        int up=0,low=0;
        for(int i=tt+1;i<=n;i++)
        {
            if(check(up,low,line[tt],line[i]))
            {
                if(dis[tt]+DIS(line[tt],line[i])<dis[i])
                {
                    dis[i]=dis[tt]+DIS(line[tt],line[i]);
                    if(!vis[i]) vis[i]=1,q.push(i); 
                }
            }
            if(((i-1)%4+1) & 1 && (!up || cross(line[tt],line[up],line[i])<=0)) up=i;
            else if(!(((i-1)%4+1)&1) && (!low || cross(line[tt],line[low],line[i])>=0)) low=i;
            if(up && low && cross(line[tt],line[up],line[low])>0) break;
        }    
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf%lf",&line[i*4-2].x,&line[i*4-2].y,&line[i*4-1].x,&line[i*4-1].y);
        line[i*4-3]=(Line){line[i*4-2].x,line[i*4-1].y};
        line[i*4]=(Line){line[i*4-1].x,line[i*4-2].y};        
    }
    n=n*4;
    scanf("%lf%lf",&s.x,&s.y);
    scanf("%lf%lf",&t.x,&t.y);
    scanf("%lf",&v);
    if(s.x>t.x) swap(s,t);
    for(;n;n--) if(line[n].x<=t.x) break;
    line[++n]=t;
    for(st=1;st<=n;st++) if(line[st].x>=s.x) break;
    line[--st]=s;
    spfa();
    printf("%.10lf",dis[n]/v);
    return 0;
}
View Code

 

[NOI2011]兔兔与蛋蛋游戏

搜索75/ 非常精彩的一道题

以空格为起点将棋盘黑白染色,将空格染成黑色,这样相邻两个点的颜色总是不一样的,等效于在二分图的两侧来回走。

那么一般的,任意给一个棋局,都可以看成一个已经走过一部分的二分图(不可能走重复的格子,因为是黑白交替走),考虑给定二分图先手是否必胜。

二者相当于走一个交错路(最后一步一定是先手走),由于最大匹配的交错路可以使得后手无路可走,所以我们开始关注最大匹配。

如果此图中的最大匹配独一无二,先手必胜;

不然就有可能被后手引上"歧途",从而无路可走。

如何判断一个点是否是非必需点?

  • 如果当前点本来就没有匹配的点,显然不在。
  • 如果当前点所匹配的点在删去当前点后,能够继续增广,就说明当前点不一定在最大匹配上,否则一定在。

 好好意会吧。。。

CODE:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=50003;
const int maxm=5e6+10;
struct point
{
    int x,y;
    int to,nxt;
}edge[maxm]; 
int n,m,k,bx,by,cnt,tot;
int map[203][203],ban[maxn],match[maxn];
int vis[maxn],viss[203][203],head[maxn],ans[maxn];
char ss[maxn];
int derx[]={0,0,0,1,-1};
int dery[]={0,1,-1,0,0};

inline void add(int u,int v)
{
    tot++;
    edge[tot].nxt=head[u];
    edge[tot].to=v;
    head[u]=tot;
}

inline int iu(int x,int y)
{
    return (x-1)*m+y;
}

inline void Bfs()
{
    queue<point> q;
    memset(viss,0,sizeof(viss));
    q.push((point){bx,by,0,0});
    while(!q.empty())
    {
        point e=q.front();
        q.pop();
        for(int i=1;i<=4;i++)
        {
            int xx=e.x+derx[i],yy=e.y+dery[i];
            if(xx<1 || xx>n || yy<1 || yy>m || !(map[e.x][e.y]^map[xx][yy])) continue;
            add(iu(e.x,e.y),iu(xx,yy));    
            add(iu(xx,yy),iu(e.x,e.y));    
            if(!viss[xx][yy])
            {
                q.push((point){xx,yy,0,0});
                viss[xx][yy]=1;
            }
        }
    }
}

inline bool dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(vis[v] || ban[v]) continue;
        vis[v]=1;
        if(!match[v] || dfs(match[v]))
        {
            match[v]=x;
            match[x]=v;
            return true;
        }
    }
    return false;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ss+1);
        for(int j=1;j<=m;j++)
        {
            if(ss[j]=='.')
            {
                bx=i,by=j;
                map[i][j]=1;
            }
            else if(ss[j]=='X') map[i][j]=1;
            else if(ss[j]=='O') map[i][j]=0;
        }
    }
    Bfs();
    for(int i=1;i<=n*m;i++)
        if(!match[i])
        {
            memset(vis,0,sizeof(vis));
            dfs(i);
        }
    scanf("%d",&k);
    for(int i=1;i<=k;i++)
    {
        int cn=iu(bx,by),a1=0,a2=0;
        ban[cn]=1;
        if(match[cn])
        {
            int op=match[cn];
            match[op]=0; match[cn]=0;
            memset(vis,0,sizeof(vis));
            a1=!dfs(op);
        }
        else a1=0;
        scanf("%d%d",&bx,&by);     
        cn=iu(bx,by);
        ban[cn]=1;
        if(match[cn])
        {
            int op=match[cn];
            match[op]=0; match[cn]=0;
            memset(vis,0,sizeof(vis));
            a2=!dfs(op);
        }
        else a2=0;
        if(a1 && a2) ans[++cnt]=i;
        scanf("%d%d",&bx,&by);
    }
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
    return 0;
}
View Code

 

 

[NOI2011]道路修建

一遍dfs就行了,NOIP之前做的题 o_O

//long long 害死人 
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=1e6+10;
struct point
{
    int to;
    long long w;
    int nxt;
}edge[maxn<<1];
int n,tot;
int head[maxn],S[maxn];
long long ans=0;

inline void add(int u,int v,long long w)
{
    tot++;
    edge[tot].to=v;
    edge[tot].nxt=head[u];
    edge[tot].w=w;
    head[u]=tot;
}

inline void dfs(int x,int fa)
{
    S[x]=1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v!=fa)
        {
            dfs(v,x);
            ans+=(long long)abs(n-S[v]*2)*edge[i].w;
            S[x]+=S[v];
        }    
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        long long w;
        scanf("%d%d%lld",&a,&b,&w);
        add(a,b,w);
        add(b,a,w);
    }
    dfs(1,0);
    printf("%lld",ans);
    return 0;
}
View Code

 

 

posted @ 2018-06-01 08:49  Captain_fcj  阅读(272)  评论(0编辑  收藏  举报