高端的N 校联考题

来源:互联网 发布:linux临时设置环境变量 编辑:程序博客网 时间:2024/03/29 16:18

序列

【问题描述】
作为一名火星人,你为了占领地球,需要想方设法使地球人失去信心。现在你获得了一项能力,控制今后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);}
0 0
原创粉丝点击