【题解】毒瘤 OI 刷题汇总 [SCOI2010]

【题解】毒瘤 OI 刷题汇总 [SCOI2010]

由于不清楚题目顺序,就按照 \(\text{BZOJ}\) 上面的排列好了。


【Day1 T1】幸运数字

传送门:幸运数字 \(\text{[P2567]}\) \(\text{[Bzoj1853]}\)

【题目描述】

只由 \(6,8\) 组成的十进制数为幸运数字,幸运数字的倍数均为近似幸运数字(包括自己),求 \([L,R]\) \((1 \leqslant L \leqslant R \leqslant 10^{10})\) 中近似幸运数字的个数。

【分析】

直接大力暴搜+剪枝搞容斥就可以水过去(貌似正解就是这样)。

先搜索求出 \(R\) 以内所有的幸运数字,并且把存在幸运数字因子的数全部筛掉,剩下的从大到小排序(后面暴搜时剪枝更强),存到数组 \(\{A\}\) 中。

\(cnt(x)=\lfloor \frac{R}{x} \rfloor-\lfloor \frac{L-1}{x} \rfloor\)\([L,R]\)\(x\) 倍数的个数),求解答案时上一个小学生容斥就可以了:\(ans=\sum_{i\in \{A\}}cnt(i)-\sum_{i,j\in\{A\},i\neq j}cnt(\operatorname{lcm}(i,j))+\sum_{i,j,k\in\{A\},i\neq j\neq k}cnt(\operatorname{lcm}(i,j,k))...\)

时间复杂度:\(O(\text{玄学})\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL unsigned long long
#define Re register LL
using namespace std;
const int N=1e5+3;
LL l,r,t,n,Ans,A[N];bool vis[N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
inline void dfs(Re x){
    if(x>r)return;
    A[++t]=x,dfs(x*10+6),dfs(x*10+8);
}
inline LL gcd(Re a,Re b){return !b?a:gcd(b,a%b);}
inline void dfs_(Re w,Re cnt,Re lcm){
    if(lcm>r)return;
    if(w>n){
        if(cnt)Ans+=((cnt&1)?1:-1)*(r/lcm-(l-1)/lcm);
        return;
    }
    dfs_(w+1,cnt,lcm),lcm/=gcd(lcm,A[w]);
    if(lcm*A[w]<=r)dfs_(w+1,cnt+1,lcm*A[w]);
}
int main(){
//    freopen("123.txt","r",stdin);
    in(l),in(r),dfs(6),dfs(8);
    sort(A+1,A+t+1);
    for(Re i=1;i<=t;++i)if(!vis[i]){
        A[++n]=A[i];
        for(Re j=i+1;j<=t;++j)vis[j]|=(A[j]%A[i]==0);
    }
    for(Re i=1;i<=n/2;++i)swap(A[i],A[n-i+1]);
    dfs_(1,0,1),printf("%llu\n",Ans);
}

【Day1 T2】游戏

传送门:游戏 \(\text{[P1640]}\) \(\text{[Bzoj1854]}\)

【题目描述】

给出 \(n\) \((n\leqslant 10^6)\) 个装备,每个装备带有两种攻击力 \(a_i,b_i\) \((1 \leqslant a_i,b_i \leqslant 10000)\),先需要先使用攻击力为 \(1\) 的装备,再使用攻击力为 \(2\) 的,再使用攻击力为 \(3\)\(...\) 依次递推,每个装备只可选择一种攻击力并且只能使用一次,求最多能攻击多少次。

【分析】

匈牙利暴力匹配判断。

发现装备和攻击力可以被划分为两个集合,且同集合内的点互不相干,即二分图。

从小到大枚举攻击力,当无法找到合法匹配点时就输出。

时间复杂度:\(O(k*\text{玄学})\),其中 \(k\) 为最大攻击次数(即答案),\(\text{玄学}\) 为每次匹配跑的边数(最大为 \(m\))。

快得飞起

注意不要用 \(memset\)

【Code】

#include<cstring>
#include<cstdio>
#define Re register int
const int N=1e6+3;
int n,x,y,o,ans,pan[N],tmp[N],head[N],match[N];
struct QAQ{int to,next;}a[N<<1];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline int dfs(Re x){
    for(Re i=head[x],to;i;i=a[i].next)
        if(!pan[to=a[i].to]){
            pan[to]=1,tmp[++tmp[0]]=to;
            if(!match[to]||dfs(match[to])){
                match[to]=x;return 1;
            }
        }
    return 0;
}
int main(){
    in(n);
    for(Re i=1;i<=n;++i)in(x),in(y),add(x,i),add(y,i);
    for(Re i=1;i<=10000;++i){
        for(Re j=1;j<=tmp[0];++j)pan[tmp[j]]=0;
        tmp[0]=0;
        if(dfs(i))++ans;
        else break;
    }
    printf("%d",ans);
}

【Day1 T3】股票交易

传送门:股票交易 \(\text{[P2569]}\) \(\text{[Bzoj1855]}\)

【题目描述】

初始时有用不完的钱(拿来烧掉造永动机?),并且每天持有的股票数不能超过 \(P\) \((P\leqslant 2000)\) 。一共有 \(n\) \((n \leqslant 2000)\) 天,第 \(i\) 天的股票买入价格为 \(AP_i\),卖出价格为 \(BP_i\) \((AP_i,BP_i \leqslant 1000)\), 买入股票的数量限制为 \(AS_i\),卖出数量限制为 \(BS_i\) \((AS_i,BS_i \leqslant P)\) 。相邻两次卖卖交易之间必须间隔 \(W\) 天。求第 \(n\) 天结束后最大收益是多少。

【分析】

暴力 \(dp\) + 单调队列优化。

\(dp[i][j]\) 为第 \(i\) 天持有 \(j\) 个股票的最大收益,\(I=(i-W-1,0)\),分三种情况转移

  • 不买也不卖:\(dp[i][j]=dp[i-1][j]\)

  • 买入:\(dp[i][j]=\max\{dp[I][k]-(j-k)*AP_i)\}\) \((k\in[j-AS_i,j])\)

  • 卖出:\(dp[i][j]=\max\{dp[I][k]+(k-j)*BP_i\}\) \((k\in[j+1,j+BS_i,P])\)

答案为 \(\max\{dp[n][j]\}\) \((j\in[0,P])\)

把式子展开发现这个东西可以用单调队列优化,随便瞎搞搞就好了。

时间复杂度:\(O(nP)\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=2005;
int n,h,t,P,W,ans,Q[N],AP[N],BP[N],AS[N],BS[N],dp[N][N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(P),in(W);
    for(Re i=1;i<=n;++i)in(AP[i]),in(BP[i]),in(AS[i]),in(BS[i]);
    memset(dp,-60,sizeof(dp));
    dp[0][0]=0;
    for(Re i=1;i<=n;++i){
        Re I=max(i-W-1,0);
        for(Re j=0,h=1,t=0;j<=P;++j){
            dp[i][j]=dp[i-1][j];
            while(h<=t&&Q[h]<j-AS[i])++h;
            if(h<=t)dp[i][j]=max(dp[i][j],dp[I][Q[h]]+Q[h]*AP[i]-j*AP[i]);
            while(h<=t&&dp[I][Q[t]]+Q[t]*AP[i]<=dp[I][j]+j*AP[i])--t;
            Q[++t]=j;
//          for(Re k=max(j-AS[i],0);k<=j;++k)
//              dp[i][j]=max(dp[i][j],dp[I][k]+k*AP[i]-j*AP[i]);
        }
        for(Re j=P,h=1,t=0;j>=0;--j){
            while(h<=t&&Q[h]>j+BS[i])++h;
            if(h<=t)dp[i][j]=max(dp[i][j],dp[I][Q[h]]+Q[h]*BP[i]-j*BP[i]);
            while(h<=t&&dp[I][Q[t]]+Q[t]*BP[i]<=dp[I][j]+j*BP[i])--t;
            Q[++t]=j;
//          for(Re k=j+1;k<=j+BS[i]&&k<=P;++k)
//              dp[i][j]=max(dp[i][j],dp[I][k]+k*BP[i]-j*BP[i]);
        }
    }
    for(Re j=0;j<=P;++j)ans=max(ans,dp[n][j]);
    printf("%d\n",ans);
}

【Day2 T1】字符串

传送门:字符串 \(\text{[P1641]}\) \(\text{[Bzoj1856]}\)

【题目描述】

现要用 \(n\)\(1\)\(m\)\(0\) 组成字符串 \((1\leqslant m \leqslant n\leqslant 10^6)\),并要求对于任意前 \(k\) \((k\in[1,n+m])\) 个字符均满足 \(1\) 的个数不少于 \(0\) 的个数,求合法字符串个数,答案对 \(20100403\) 取膜。

【分析】

\(20100403\) 是个质数,直接暴力组合数计算答案即可。

类似卡特兰的思路,画一个坐标系出来瞎推推就好了,答案为 \(C^{m}_{n+m}-C_{n+m}^{m-1}\)

时间复杂度:\(O(n)\)

【Code】

#include<cstdio>
#define LL long long
#define Re register int
const int N=2e6+3,P=20100403;
int n,m,jc[N],inv[N],invjc[N];
inline int C(Re m,Re n){return (LL)jc[n]*invjc[n-m]%P*invjc[m]%P;}
int main(){
//    freopen("123.txt","r",stdin);
    scanf("%d%d",&n,&m),jc[0]=inv[1]=invjc[0]=invjc[1]=1;
    for(Re i=2;i<=(n+m);++i)inv[i]=(LL)inv[P%i]*(P-P/i)%P;
    for(Re i=1;i<=(n+m);++i)jc[i]=(LL)jc[i-1]*i%P,invjc[i]=(LL)invjc[i-1]*inv[i]%P;
    printf("%d\n",(C(m,n+m)-C(m-1,n+m)+P)%P);
}

【Day2 T2】传送门

传送门:传送门 \(\text{[P2571]}\) \(\text{[Bzoj1857]}\)

【题目描述】

略。

【分析】

毒瘤计算几何,貌似可以用模拟退火,不想做。

【Code】

不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)

【Day2 T3】序列操作

传送门:序列操作 \(\text{[P2572]}\) \(\text{[Bzoj1858]}\)

【题目描述】

给出一个长为 \(n\) \((n\leqslant 10^5)\)\(01\) 序列,共有 \(5\) 种操作:

  • \(0\ l\ r\) 把区间 \([l,r]\) 内的所有数全变成 \(0\)

  • \(1\ l\ r\) 把区间 \([l,r]\) 内的所有数全变成 \(1\)

  • \(2\ l\ r\) 把区间 \([l,r]\) 内的所有数全部取反,也就是把所有的 \(0\) 变成 \(1\),把所有的 \(1\) 变成 \(0\)

  • \(3\ l\ r\) 询问区间 \([l,r]\) 内总共有多少个 \(1\)

  • \(4\ l\ r\) 询问区间 \([l,r]\) 内最多有多少个连续的 \(1\)

【分析】

珂朵莉大法好

不想码线段树,敲颗珂朵莉树水了过去。

一道板子题,没啥好说的。

时间复杂度:\(O(\text{玄学})\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<set>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3;
int n,x,y,T,op,A[N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct ODT{
    #define IT set<QAQ>::iterator
    struct QAQ{
        int l,r;mutable int v;
        QAQ(int L,int R=-1,int V=0):l(L),r(R),v(V){}
        inline bool operator<(const QAQ &O)const{return l<O.l;}
    };
    set<QAQ>s;
    inline void build(){
        Re w=1;
        for(Re i=2;i<=n;++i)if(A[i]!=A[i-1])s.insert(QAQ(w,i-1,A[i-1])),w=i;
        s.insert(QAQ(w,n,A[n]));
        s.insert(QAQ(n+1,n+1,0));
    }
    inline IT split(Re w){
        IT it=s.lower_bound(QAQ(w));
        if(it!=s.end()&&it->l==w)return it;
        --it;
        Re L=it->l,R=it->r,V=it->v;
        s.erase(it),s.insert(QAQ(L,w-1,V));
        return s.insert(QAQ(w,R,V)).first;
    }
    inline void assign(Re l,Re r,Re v){
        IT itr=split(r+1),itl=split(l);
        s.erase(itl,itr),s.insert(QAQ(l,r,v));
    }
    inline void fan(Re l,Re r){
        IT itr=split(r+1),itl=split(l);
        while(itl!=itr)itl->v^=1,++itl;
    }
    inline int cnt1(Re l,Re r){
        IT itr=split(r+1),itl=split(l);Re ans=0;
        while(itl!=itr)ans+=itl->v*(itl->r-itl->l+1),++itl;
        return ans;
    }
    inline int ask(Re l,Re r){
        IT itr=split(r+1),itl=split(l);Re ans=0,now=0;
        while(itl!=itr){
            if(itl->v)now+=itl->r-itl->l+1,ans=max(ans,now);
            else now=0;
            ++itl;
        }
        return ans;
    }
}T1;
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(T);
    for(Re i=1;i<=n;++i)in(A[i]);
    T1.build();
    while(T--){
        in(op),in(x),in(y),++x,++y;
        if(op<2)T1.assign(x,y,op);
        else if(op<3)T1.fan(x,y);
        else if(op<4)printf("%d\n",T1.cnt1(x,y));
        else printf("%d\n",T1.ask(x,y));
    }
}

posted @ 2020-04-15 20:13  辰星凌  阅读(276)  评论(0编辑  收藏  举报