高端的N 校聯考題

序列

【問題描述】
作爲一名火星人,你爲了佔領地球,需要想方設法使地球人失去信心。現在你獲得了一項能力,控制今後n天的天氣溫度,對於第i天,你能將溫度控制在[ai,bi]中任意一個數字,你的目的是使其中某段時間,溫度持續不下降,趁此來攻擊地球。現在問你最多可以使連續的多少天滿足溫度不下降。
【輸入】 第一行給出一個整數n,表示你能控制的天數。 接下來n行,第i行給出2個整數ai,bi,表示你能控制的天氣範圍。保證ai<=bi。
【輸出】 輸出一個整數,表示答案。
【輸入輸出樣例】
sequence.in
4
1 3
2 4
1 1
3 4
sequence.out
2
【數據範圍】
對於20%的數據 3<=n<=10;
對於40%的數據 3<=n<=3000;
對於60%的數據 3<=n<=100000;
對於100%的數據 3<=n<=1000000,1<=ai,bi<=100000。

先以爲是和如lis一樣的簡單dp
然後就那樣寫了 呵呵 出題人數據不卡這個 80……
因爲如果從第一個開始可能並不一定是最優解
所以用這個思想 還要加上單調隊列來維護

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

void init()
{
  freopen("sequence.in","r",stdin);
  freopen("sequence.out","w",stdout);
}

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;
}

int n;
int a[1000005],b[1000005];
int ans=1;
int q[1000005];
int q_top=1,q_bot=0;
int main(){
  init();
  n = read();
  for(int i=1 ;i<=n ;i++)
  {a[i]=read();b[i]=read();}  

  for(int i=1 ;i<=n ;i++)
  {  
     while(q_top <= q_bot && a[q[q_top]] > b[i])
     ++q_top;
     ans = max(ans , i - q[q_top-1]);
     while(q_top <= q_bot && a[i]>= a[q[q_bot]])
     --q_bot;
     q[++q_bot] = i;    
  }  

  printf("%d",ans);
  return 0;
}

循環整數

【問題描述】
moreD在學習完循環小數之後發現循環是個很美好的性質。自己只需要記住短短的循環節以及循環次數(次數大於1,且是整數)就可以記住整個數字了。
因爲背誦數字變得方便了,moreD決定背誦[L,R]內的所有循環的整數。moreD的背誦計劃有T天,但是他不知道每天具體要背多少個數,請你幫助moreD計算出每天需要背誦的數字個數。
如果moreD在某天遇到一個曾經背過的數字,他會義無反顧地重新背誦。
【輸入格式】 第一行給出一個整數T,表示moreD計劃背誦T天的數字。 接下來n行,第i行給出2個整數Li,Ri,表示moreD第i天的背誦計劃。
【輸出格式】 輸出T行,每行一個整數,表示第i天moreD需要背誦的數字個數。
【輸入輸出樣例】
circulate.in
3
1 10000
55555 66666
10 100
circulate.out
108
2
9
【數據範圍】
對於30%的數據 T*MAX{Ri}<=2*10^6
對於70%的數據MAX{Ri}<=2*10^6
對於100%的數據 T<=50000,1<=Li<=Ri<=2*10^18
【樣例解釋】
對於第2天,moreD只需要背誦55555,66666.
對於第3天,moreD只需要背誦11,22,33,44,55,66,77,88,99.

我只知道我做了2個小時也沒做出來
當然可以暴力其實那點小數據還是可以的
以下是題解

對於[L,R]內的循環整數個數,可以看成是[1,R]內的循環整數個數減去[1,L-1]內的循環整數個數。
{對於確定了循環節長度i以及數字長度n的循環整數,在[1,x]內的個數可以用MAX{x div k-10^(i-1)+1,0}算出,其中k是i-1個0,1個1,循環n div i次所得的數字。
例如,若求[1,666666]內長度爲6的循環節長度爲3的數字只需要用666666 div 1001-100+1=567.如此即可快速算出數字個數。這個公式成立當且僅當x的數字長度等於n。也就是說[1,666666]內長度爲5的循環節長度爲1的數字等於99999 div 11111-1+1=9.不能使用666666作爲被除數.}(暴力枚舉長度判斷也可.}
所以就可以枚舉循環節長度以及數字長度,利用以上公式算出循環整數的數字個數。
但是,很容易發現,這種做法會引起重複計算。因爲如果一個整數的循環節長度是i,那隻要k*i是數字長度的約數,k*i也是該整數的循環節長度,所以每次計算循環節長度爲i的循環整數個數的時候也要把循環節長度爲i的約數的所有合法循環整數減去。
也就是說,9999在循環節長度是1的時候已經算了一遍,而在循環節長度爲2的時候也會算一次,所以要減去。
時間複雜度爲O(T*(lgMAX{R})^2),期望得分100%。

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

void init()
{
  freopen("circulate.in","r",stdin);
  freopen("circulate.out","w",stdout);
}
int ans=0;

inline long long read(){
  long long 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;
}
/*-----------------------------------------------------*/

const int MaxNL = 20;
long long tab[MaxNL];
long long orz[MaxNL];
//hard to think 
inline long long count(long long x)
{
    int l = 0, d[MaxNL];
    while (x > 0)
        d[++l] = x % 10, x /= 10;//!各!位數 

    long long  ans = 0;
    for (int i = 1; i < l; ++i)
        ans += tab[i]; //滿方案數 

    long long s = 1;
    for (int j = 1; j < l; ++j, s *= 10)
        if (l % j == 0)
        {
            long long t = 0;
            for (int i = l; i > l - j; --i)
                t = t * 10 + d[i];

            long long p = 0;
            bool cutHand = false;
            for (int i = l - j; i > 0; --i)
            {
                p = p * 10 + d[i];
                if (i % j == 1 || j == 1)
                {
                    if (p < t)
                        cutHand = true;
                    else if (p > t)
                        break;
                    p = 0;
                }
            }

            orz[j] = t - s - cutHand + 1;
            for (int k = 1; k < j; ++k)
                if (j % k == 0)
                    orz[j] -= orz[k];
            ans += orz[j];
        }
    return ans;
}

void prepare(){
for (int i = 1; i < MaxNL; ++i)  //位數 
    {
        long long s = 9;          // 首位 
        for (int j = 1; j < i; ++j)//枚舉約數 可循環位數 
            {
            if (i % j == 0)
            {
                orz[j] = s;
                for (int k = 1; k < j; ++k)
                    if (j % k == 0)
                        orz[j] -= orz[k]; //減去約數的約數 
                tab[i] += orz[j];//加上當前方案數 
            }
            s *= 10;
            }
    }
}

int T;
long long l,r;
int main(){
    init();
    prepare();
    /*
    for(int i = 1 ;i<=18 ;i++)printf("%d ",tab[i]);
    printf("\n");
    for(int i = 1 ;i<=18 ;i++)printf("%d ",orz[i]);
    /**/
    T=read();
    while(T--){
      l=read();r=read();
      long long ans = count(r) - count(l-1);
      printf("%lld\n",ans);
    } 
return 0;
}

小Y的炮

【問題描述】
小Y最近開發出了批量製造大威力轟山炮的方法。纔過去不到幾個月,小Y就製造出了M門款式不同的轟山炮。第i門轟山炮發射一次,能使一座當前高度不高於Ai的山的高度降低Di(當然山的高度不能轟到0以下)。應政府要求,小Y要用他開發的轟山炮轟平開發區的幾座山。由於開發區急需土地資源,政府要求小Y轟平儘量多的山(轟平:使山的高度降低至0)。
但是小Y製造的彈藥有限,導致他最多隻能發射K次。
小Y想知道,他最多能轟平幾座山?轟平這些山後,彈藥最多還夠他發射幾次?
【輸入】
第一行三個正整數N,M,K,分別表示山的數目、轟山炮的款式數目、最多發射次數。
接下來N行,每行一個正整數Hi,表示第i座山的高度,輸入數據保證Hi是降序的(從大到小)。
接下來M行,每行兩個正整數Ai,Di,分別表示第i款轟山炮能轟的山的最高高度,和轟掉的山高度的減少值。
【輸出】
一行兩個整數Max,Remain,分別表示最多轟平的山的數目和轟平這些山後最多的剩餘發射次數。
【輸入輸出樣例】
cannon.in
3 2 3
8
6
2
10 6
6 5
cannon.out
2 1
【數據範圍】
20%的數據滿足N<=100,M<=100,Hi,Ai<=100。
50%的數據滿足N<=1000,M<=500。
80%的數據滿足N<=250000,M<=500。
20%、50%、80%的數據均滿足Hi,Ai<=1000000。
100%的數據滿足N<=250000,M<=500,K,Hi,Ai<=10^18,Di<=500。

貪心當然是一種優秀的策略
注意判斷一些不必要的條件50還是可以的

當然正解不是這樣的
以下
100%:注意到D的範圍很小。假如一個高度H在區間i內,一直降低,在H剛好降低到剛好比A[i-1]小時,總有A[i-1]-H<=D。對於任何高度都有上述結論。所以我們最多隻需處理O(M*D)個高度即可。令F[H]爲轟平H高度的山的最少發射次數,則對於某個高度H,一直降至H-t*Di<=A[i-1],有F[H]=F[H-t*Di]+t,由於有用的H最多隻有O(M*D)個,所以這樣做的效率爲O(M*D)。(F數組可由HASH代替)
那麼對於從低到高的每座山,設該山高度爲H,找到H對應的區間i(O(N+M)),將H降至H-t*Di<=A[i-1],那麼最少發射次數爲t+F[H-t*Di],其中H-t*Di必然是O(M*D)個高度中的一個。所以對於每座可以被轟的山,都可以用O(1)的時間得到轟平所需的最少發射次數。

不完全正確的代碼
(不是我寫的)………
因爲我還沒全對

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;

void init()
{
  freopen("cannon.in","r",stdin);
  freopen("cannon.out","w",stdout);
}
const int maxn=250004;
struct data{
    ll a,d;
    bool operator <(const data &b)const{
        return (a<b.a || (a==b.a && d<b.d));
    }
}cnn[maxn],p[maxn];
ll k,h[maxn],ans=0;
int n,m,cnt=0;
map<ll,ll>f;

inline ll get(){
    char c;while(!isdigit(c=getchar()));
    ll v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
    return v;
}
int main(){
    init();
    n=get();m=get();k=get();
    for(int i=n;i>=1;--i)h[i]=get();
    for(int i=1;i<=m;++i)cnn[i].a=get(),cnn[i].d=get();
    sort(cnn+1,cnn+1+m);
    for(int i=1;i<=m;++i){
        while(cnt && p[cnt].d<=cnn[i].d)--cnt;
        p[++cnt]=cnn[i];
    }
    ll dn=0;f[0]=0;
    for(int i=1;i<=cnt;++i){
        if(i!=1)dn=p[i-1].a;
        ll l=max(dn,p[i].a-p[i].d);
        for(ll j=l+1;j<=p[i].a;++j){
            ll t=(j-dn-1)/p[i].d+1;
            f[j]=f[max(0ll,j-(p[i].d*t))]+t;
        }
    }
    int j=1;
    dn=0;
    for(int i=1;i<=n;++i){
        while(j<=cnt && p[j].a<h[i])++j;
        if(j>cnt)break;
        if(j!=1)dn=p[j-1].a;
        ll t=(h[i]-dn-1)/p[j].d+1;
        ll q=f[max(0ll,h[i]-(p[j].d*t))]+t;
        if(k>=q)k-=q,++ans;else break;
    }
    printf("%lld ",ans);
    printf("%lld\n",k);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章