【题解】毒瘤 OI 刷题汇总 [SCOI2010]
由于不清楚题目顺序,就按照 ( ext{BZOJ}) 上面的排列好了。
【Day1 T1】幸运数字
传送门:幸运数字 ( ext{[P2567]}) ( ext{[Bzoj1853]})
【题目描述】
只由 (6,8) 组成的十进制数为幸运数字,幸运数字的倍数均为近似幸运数字(包括自己),求 ([L,R]) ((1 leqslant L leqslant R leqslant 10^{10})) 中近似幸运数字的个数。
【分析】
直接大力暴搜+剪枝搞容斥就可以水过去(貌似正解就是这样)。
先搜索求出 (R) 以内所有的幸运数字,并且把存在幸运数字因子的数全部筛掉,剩下的从大到小排序(后面暴搜时剪枝更强),存到数组 ({A}) 中。
设 (cnt(x)=lfloor frac{R}{x} floor-lfloor frac{L-1}{x} floor)(([L,R]) 中 (x) 倍数的个数),求解答案时上一个小学生容斥就可以了:(ans=sum_{iin {A}}cnt(i)-sum_{i,jin{A},i eq j}cnt(operatorname{lcm}(i,j))+sum_{i,j,kin{A},i eq j eq k}cnt(operatorname{lcm}(i,j,k))...)
时间复杂度:(O( ext{玄学})) 。
【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
",Ans);
}
【Day1 T2】游戏
传送门:游戏 ( ext{[P1640]}) ( ext{[Bzoj1854]})
【题目描述】
给出 (n) ((nleqslant 10^6)) 个装备,每个装备带有两种攻击力 (a_i,b_i) ((1 leqslant a_i,b_i leqslant 10000)),先需要先使用攻击力为 (1) 的装备,再使用攻击力为 (2) 的,再使用攻击力为 (3) 的 (...) 依次递推,每个装备只可选择一种攻击力并且只能使用一次,求最多能攻击多少次。
【分析】
匈牙利暴力匹配判断。
发现装备和攻击力可以被划分为两个集合,且同集合内的点互不相干,即二分图。
从小到大枚举攻击力,当无法找到合法匹配点时就输出。
时间复杂度:(O(k* ext{玄学})),其中 (k) 为最大攻击次数(即答案),( ext{玄学}) 为每次匹配跑的边数(最大为 (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】股票交易
传送门:股票交易 ( ext{[P2569]}) ( ext{[Bzoj1855]})
【题目描述】
初始时有用不完的钱(拿来烧掉造永动机?),并且每天持有的股票数不能超过 (P) ((Pleqslant 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)}) ((kin[j-AS_i,j]))
-
卖出:(dp[i][j]=max{dp[I][k]+(k-j)*BP_i}) ((kin[j+1,j+BS_i,P]))
答案为 (max{dp[n][j]}) ((jin[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
",ans);
}
【Day2 T1】字符串
传送门:字符串 ( ext{[P1641]}) ( ext{[Bzoj1856]})
【题目描述】
现要用 (n) 个 (1) 和 (m) 个 (0) 组成字符串 ((1leqslant m leqslant nleqslant 10^6)),并要求对于任意前 (k) ((kin[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
",(C(m,n+m)-C(m-1,n+m)+P)%P);
}
【Day2 T2】传送门
传送门:传送门 ( ext{[P2571]}) ( ext{[Bzoj1857]})
【题目描述】
略。
【分析】
毒瘤计算几何,貌似可以用模拟退火,不想做。
【Code】
不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)
【Day2 T3】序列操作
传送门:序列操作 ( ext{[P2572]}) ( ext{[Bzoj1858]})
【题目描述】
给出一个长为 (n) ((nleqslant 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( ext{玄学}))。
【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
",T1.cnt1(x,y));
else printf("%d
",T1.ask(x,y));
}
}