NOIP 2011TG 解題報告

這兩天做了noip2011 的題。
於是來寫寫題解。
若有錯誤請大家指出。。。

DAY 1
1、
直接判斷點是否在矩形內,看最後一次是哪一張就是了,注意邊界。
或者你也可以倒着來 注意 break;
反正對於矩形 四點的座標確定也就確定了

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
#define mem(a,x) memset(a,0,sizeof(a))

struct node{
  int x,y,a,b;
}q[10050];
int n;
int tax,tay;
int ans;

void readdata(){
  scanf("%d",&n);
  for( int i=1 ;i<=n ;i++)
  {
    scanf("%d%d%d%d",&q[i].x,&q[i].y,&q[i].a,&q[i].b);
  }
  scanf("%d%d",&tax,&tay);
}

int main(){
   readdata();
   for(int i=1; i<=n; i++)
   {
      if(q[i].x<=tax && q[i].x+q[i].a>=tax && q[i].y<=tay && q[i].y+q[i].b>=tay)
      ans=i;
   }
  if(ans == 0)printf("-1");
  else printf("%d",ans);
  return 0;
}

2、
此題首先得理清題意。
好然後誰都一定可以來一個暴力。
然後 你也可以來一個線段樹 但有些注意條件

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

using namespace std;
#define GETMID int mid = (l + r) >> 1
#define lson u << 1
#define rson u << 1 | 1
#define Lson u << 1, l, mid
#define Rson u << 1 | 1, mid + 1, r
#define Maxn (200000 + 10)

int n, k, p;
int pri[Maxn];
int col[51][200001];
int kcnt = 0;
int pos[51];
int vis[51] = {0};
long long Ans = 0;

int mi[Maxn * 4];


void Build(int u, int l, int r) {
    if(l == r) {
        mi[u] = pri[l];
        return;
    }
    GETMID;
    Build(Lson);
    Build(Rson);
    mi[u] = min(mi[lson], mi[rson]);
}

int Query(int u, int l, int r, int L, int R) {
    if(L <= l && r <= R) return mi[u];
    GETMID;
    int ret = 1000;
    if(L <= mid) ret = min(ret, Query(Lson, L, R));
    if(R >  mid) ret = min(ret, Query(Rson, L, R));
    return ret;
}

int main() {
    scanf("%d%d%d", &n, &k, &p);
    for(int i = 1; i <= n; ++i) {
        int x;
        scanf("%d%d", &x, &pri[i]);
        if(!vis[x]) {
            vis[x] = 1;
            col[++kcnt][++col[kcnt][0]] = i;
            pos[x] = kcnt;
        }
        else col[pos[x]][++col[pos[x]][0]] = i;
    }
    Build(1, 1, n);

    int last, ns, ret;
    for(int i = 1; i <= kcnt; ++i) {
        last = 1;
        for(int j = 1; j <= col[i][0]; ++j) {
            ret = Query(1, 1, n, col[i][last], col[i][j]);
            if(ret > p) continue;

            Ans += (long long)(j - last) * (long long)(col[i][0] - j + 1);
            last = j;
        }
    }

    printf("%I64d\n", Ans);

    return 0;
}

然後你還可以想一下,得到一些簡單的方法。
維護前綴和,count[i][j]爲前1..i顏色j的數量,記錄最後出現ok[i]=1的i。如果一個區間可以由靠左的ok[i]滿足,一定可以由區間內靠右的ok[i]滿足,不妨讓最靠右的ok[i]滿足。從1到n枚舉區間右端點。通過前綴和計算左端點的方案數。
我覺得用兩個數組記比較好。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int n,ys,p;
bool ok,f;
int ans=0;
int k[200005],c[200005];
int y[51]={0},s[51]={0},w[51];

int main()
{   
    memset(w,-1,sizeof(w));
    scanf("%d%d%d",&n,&ys,&p);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&k[i],&c[i]);

    for(int i=1;i<=n;i++){
        ok=0;
        if(c[i]<=p)ok=1;
        for(int j=i+1;j<=n;j++){
            if(c[j]<=p)ok=1;
            if(k[j]==k[i]){
                if(ok){
                    ok=0;w[k[i]]=-1;y[k[i]]++;s[k[i]]+=y[k[i]];}
                else{
                    if(w[k[i]]==-1){w[k[i]]=y[k[i]];}               
                    s[k[i]]+=w[k[i]];
                    y[k[i]]++;
                }
                break;}
        }
    }
        for(int i=0;i<ys;i++)
        ans+=s[i];
    printf("%d",ans);
    return 0;
}

3、
這是一道搜索 dfs
不過真的是要打好多啊
判重 剪枝什麼的一定要做好

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
struct node
{
    int t[6];
    int c[6][8];
    node()
    {
        memset(t,0,sizeof(t));
        memset(c,0,sizeof(c));
    }
};

int n,ans[6][3];
bool flag,b[6][8],Istrue=false;
node solve(node g)//將標記不用消除的格子依次碼好,即完成了清除 
{
    node h;
    for(int i=1;i<=5;i++)
    {
        for(int j=1;j<=g.t[i];j++)
        {
            if(g.c[i][j]&&!b[i][j])
            {
                h.t[i]++;
                h.c[i][h.t[i]]=g.c[i][j];
            }
        }
    }
    return h;
}
node clear(node g)
{
    node h;
    flag=false;
    memset(b,0,sizeof(b));
    for(int i=1;i<=5;i++)//每列 
    {
        for(int j=1;j<=g.t[i];j++)//每列從下到上的棋子 
        {
            if(i<=3)//橫向
            {
                if(g.c[i][j]==g.c[i+1][j]&&g.c[i][j]==g.c[i+2][j])//連續三個相同 
                {
                    b[i][j]=b[i+1][j]=b[i+2][j]=true;//標記要消掉的格子 
                    flag=true;
                }
            }
            if(j<=g.t[i]-2)//縱向 
            {
                if(g.c[i][j]==g.c[i][j+1]&&g.c[i][j]==g.c[i][j+2])//同上 
                {
                    b[i][j]=b[i][j+1]=b[i][j+2]=true;
                    flag=true;
                }
            }
        }
    }
    if(flag)//如果有三個顏色連續的棋子 
    {
        h=solve(g);//清除 
        return clear(h);//直到不能清除爲止 
    } else return g;
}
void print()
{
    for(int i=1;i<=n;i++)
        printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
    Istrue=true;
}

void dfs(node g,int k)
{
    int l;
    node h;
    flag=true;
    for(int i=1;i<=5;i++)
    {
        if(g.t[i])//如果還沒有完全消除 
        {
            flag=false;
            break;
        }
    }
    if(flag)//如果剛好在第n步完全消除則輸出 
    {
        if(k>n) print(); else return;
    }
    if (Istrue) return ;//如果over
    if(k>n) return;//如果超過n步 
    for(int i=1;i<=5;i++)//對於每列 
    {
        for(int j=1;j<=g.t[i];j++)//每行 
        {
            if(i<5)//前4列,考慮右移的情況,最優化剪枝,右移優於左移 
            {
                if(j>g.t[i+1])//如果該列高於第後一列 ,即可以直接右移 
                {
                    h=g;//直接移動 
                    h.t[i+1]++;
                    h.c[i+1][h.t[i+1]]=h.c[i][j];
                    h.c[i][j]=0;
                    h.t[i]--;
                    for(l=j;l<=h.t[i];l++)//第i列懸空的下落 
                        h.c[i][l]=h.c[i][l+1];
                    h=clear(h);//清除 
                    ans[k][0]=i-1;//記錄所走方法 
                    ans[k][1]=j-1;
                    ans[k][2]=1;
                    dfs(h,k+1);//繼續走下一步 
                    if (Istrue) return ;
                }
                else//需要交換 
                {
                    h=g;
                    l=h.c[i][j];//交換兩個棋子 
                    h.c[i][j]=h.c[i+1][j];
                    h.c[i+1][j]=l;
                    h=clear(h);//清除 
                    ans[k][0]=i-1;//記錄方案 
                    ans[k][1]=j-1;
                    ans[k][2]=1;
                    dfs(h,k+1);//繼續走下一步 
                    if (Istrue) return ;
                }
            }

            if(i>1)//如果是後4列,需要考慮左移的情況 
            {
                if(j>g.t[i-1])//如果該列高於前一列 
                {
                    h=g;//直接移動 
                    h.t[i-1]++;
                    h.c[i-1][h.t[i-1]]=h.c[i][j];
                    h.c[i][j]=0;
                    h.t[i]--;
                    for(l=j;l<=h.t[i];l++)
                        h.c[i][l]=h.c[i][l+1];

                    h=clear(h);//清除 
                    ans[k][0]=i-1;//記錄方案 
                    ans[k][1]=j-1;
                    ans[k][2]=-1;
                    dfs(h,k+1);
                    if (Istrue) return ;
                }
            }
        }
    }
}

int main()
{
    int j;
    node g;
    scanf("%d",&n);
    for(int i=1;i<=5;i++)//讀入棋盤初始狀態 
    {
        scanf("%d",&j);
        while(j)
        {
            g.t[i]++;
            g.c[i][g.t[i]]=j;
            scanf("%d",&j);
        }
    }
    dfs(g,1);//從第一步開始搜索 
    if (!Istrue) printf("-1\n");
    return 0;
}

DAY 2
1、
數學題對吧
恩那就不多說了 注意時刻取模,還有組合數的遞推公式。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 10007;

int a,b,k,n,m;
int f[1005][1005];
int ans;
int main(){
  init();
  scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
  a%=M;  b%=M;
  for(int i=1;i<=k;i++) {f[i][i]=f[i][0]=1;f[i][1]=i;}

  for(int i=3;i<=k;i++)
        for(int j=2;j<i;j++)
            f[i][j]=(f[i-1][j]+f[i-1][j-1])%M;

  ans = f[k][m] %M;
  for(int i=1 ;i<=n ;i++) ans=(ans*a) % M;
  for(int i=1 ;i<=m ;i++) ans=(ans*b) % M;
  printf("%d",ans); 
}

2、
這道題讀完題後應該會想到二分吧。
注意初始的ans要long long 還要附一個很大很大的值。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
#define  LL long long 

LL ans=1e14; 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

LL lmax(LL x,LL y){
 if(x>y) return x;
 return y;
}
LL lmin(LL x,LL y){
 if(x<y) return x;
 return y;
}
LL labs(LL x){
 if(x>0) return x;
 return -x;
}
int mw=-1;
int n,m;
LL S;
int w[200005],v[200005];
int l[200005],r[200005];

LL sum[200005],c[200005];

LL cal(int x){
   LL tem = 0;
   for(int i=1 ;i<=n ;i++){
    sum[i] = sum[i-1];
    c[i] = c[i-1];
     if(w[i]>=x)
        {
            sum[i]+=v[i];
            c[i]++;
        }
    }
    for(int i=1;i<=m;i++)
    {
        tem+=(c[r[i]]-c[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
    }
    return tem;    
}


int main(){
  n = read(); m = read(); scanf("%I64d",&S);  
  LL mw;
  for(int i=1 ;i<=n ;i++)
  {w[i] =read();
   v[i] =read();
   mw=lmax(mw,w[i]);  
  }
  for(int i=1 ;i<=m ;i++)
  {l[i] =read();
   r[i] =read(); 
  }

  int l=0;
  int r=mw+1; 

  while(l < r){
    int mid=(l+r)>>1;
    LL t=cal(mid);
    LL cmp =labs(t-S);
    ans=lmin(ans,cmp);
    if(t<=S)r=mid;
    else l=mid+1;
  } 
  printf("%I64d",ans);
 return 0;
}

3、
最後一道題
要是沒想到的話過掉一點還是可行的。
然後這道題肯定是貪心
發現給某個D[i]減一後會使後面連續的人等車的車站的乘客提前上車。直到一個車站leave[j]>get[j](車等人),人來的時間是不能跟改變的。所以提前了上車時間的旅客提前了的一分鐘要在等人上浪費掉,所以旅行時間不會減小。也就是說令right[i]爲i後面第一個滿足車等人的站點 那麼在i+1到right[i]這個區間內下車的人的旅行時間都會減小一。維護下車人數的前綴和S。
對於K
1.k很小全部用掉
2.D[i]很小全部減掉(必須保證每個D[i]>=0)
3.i+1..tright[i]中最小的Get[j]-Leave[ j ] (把這個值減成負的會破壞現有的區間的關係,減成負的就變成車等人了)

#include<stdio.h>
#include<stdlib.h>
#define max(a,b) (a>b ? (a):(b))

long long i,j,k,n,m,l;
long long time[1100],f[1100],g[1100],sum[1100],d[1100];
long long a[11000],b[11000],t[11000],ans;

void init()
{
     long long i,j;
     scanf("%I64d%I64d%I64d",&n,&m,&k);
     for (i=1;i<n;i++)
         scanf("%I64d",&d[i]);
     for (i=1;i<=m;i++)
     {
         scanf("%I64d%I64d%I64d",&t[i],&a[i],&b[i]);
         sum[b[i]]++;
         f[a[i]]=max(f[a[i]],t[i]);
     }

     return ;
}
void in_time()
{
     long long i,j,l;

     time[1]=0;
     for (i=1;i<=n;i++)
         time[i]=max(time[i-1],f[i-1])+d[i-1];
     g[n]=n;
     g[n-1]=n;
     for (i=n-2;i>=1;i--)
         if (time[i+1]<=f[i+1])
            g[i]=i+1;
         else g[i]=g[i+1];
     for (i=1;i<=m;i++)
         ans+=time[b[i]]-t[i];
     for (i=1;i<=n;i++)
         sum[i]=sum[i-1]+sum[i];
     return ;
}
void work()
{
     long long i,j;
     long long maxs=0,x=0;

     for (i=1;i<n;i++)
         if (maxs<sum[g[i]]-sum[i]&&d[i]>0)
            maxs=sum[g[i]]-sum[i],
            x=i;
     long long l=x,r=g[x];
     d[x]--;
     ans-=maxs;
     if (r>n-1)
        r=n-1;
     for (i=l;i<=r;i++)
         time[i]=max(f[i-1],time[i-1])+d[i-1];
     for (i=r;i>=l;i--)
         if (time[i+1]<=f[i+1])
            g[i]=i+1;
         else
         g[i]=g[i+1];

     return ;
}
int main()
{   
    init();
    in_time();
    for (i=1;i<=k;i++)
    work();

    printf("%I64d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章