23号~26号的10场cf
第一场:#272 div1
A题:
推下公式然后统计,注意a/b=c,a=c*b+(a%b),比如7/3=2,7=2*3+7%3。
没什么好讲的,A题就是个水题。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define rep(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int maxn=1000100; const int INF=(1<<29); typedef long long ll; const ll MOD=1000000007; ll a,b; int main() { cin>>a>>b; ll ans=0; for(int k=1;k<=a;k++){ ll tmp=(((k*b+1)%MOD)*(((b-1)*b/2)%MOD))%MOD; ans=(ans%MOD+tmp%MOD)%MOD; } cout<<ans<<endl; return 0; }
B题:
找规律。。但是前提得知道,gcd(a,b)=c ----》 gcd(a/c,b/c)=1。
四个数的gcd为k可以让这些数除以k,这样变成k=1的情况,问题转化成找四个互质的数。K>1的时候让结果乘以K就行了。
然后就是找规律了。。。首先不能出现两个偶数,然后发现偶数出现在第二个比较好。。
1 2 3 5
7 8 9 11
13 14 15 17
19 20 21 23
.......
然后规律就很明显了。。
(6*i-5),(6*i-4),(6*i-3),(6*i-1)
首先还是要想到转换为互质简化问题,接着从前几项开始找规律,稍稍观察就能找到了。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define rep(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int maxn=1000100; const int INF=(1<<29); int n,k; int main() { while(cin>>n>>k){ cout<<(6*n-1)*k<<endl; for(int i=1;i<=n;i++){ printf("%d %d %d %d ",(6*i-5)*k,(6*i-4)*k,(6*i-3)*k,(6*i-1)*k); } } return 0; }
C题是dp题,也涉及到字符串,必须补啊。。明天早上起来再补,补完C题后直接开第二场。
C题:
感觉和上次dp专题赛的数字那题有些类似,都是前i个找j个的形式,然后进行转移,前一状态是i-1,但不一定j-1,可以是j-k。
当然dp的前提还是范围比较小。
这个可以单独写个题解:http://www.cnblogs.com/--560/p/4754253.html
第一场,爆零了。
第二场:#274 div1
A题:水题,贪心,排个序就行了。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int INF=(1<<29); const int maxn=1000100; struct Node { ll a,b; friend bool operator<(Node A,Node B) { if(A.a<B.a) return 1; if(A.a==B.a) return A.b<B.b; return 0; } }; int n; Node p[maxn]; int main() { freopen("in.txt","r",stdin); while(cin>>n){ for(int i=1;i<=n;i++){ scanf("%I64d%I64d",&p[i].a,&p[i].b); } sort(p+1,p+n+1); ll tmp=1; for(int i=1;i<=n;i++){ if(p[i].b>=tmp) tmp=p[i].b; else tmp=p[i].a; } cout<<tmp<<endl; } return 0; }
B题:水题。用几个map或set随便弄一下就行了。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int INF=(1<<29); const int maxn=1000100; ll n,l,x,y; ll a[maxn]; map<ll,int> X,Y,A; int main() { freopen("in.txt","r",stdin); while(cin>>n>>l>>x>>y){ X.clear(),Y.clear(),A.clear(); for(int i=1;i<=n;i++) scanf("%I64d",&a[i]),A[a[i]]++; bool flagX=0,flagY=0; set<ll> sX,sY; for(int i=1;i<=n;i++){ if(A[a[i]+x]||A[a[i]-x]) flagX=1; if(A[a[i]+y]||A[a[i]-y]) flagY=1; } if(flagX&&flagY) puts("0"); else{ for(int i=1;i<=n;i++){ if(a[i]+x<=l) sX.insert(a[i]+x); if(a[i]-x>=0) sX.insert(a[i]-x); if(a[i]+y<=l) sY.insert(a[i]+y); if(a[i]-y>=0) sY.insert(a[i]-y); } if(flagY){ cout<<1<<endl; cout<<*(sX.begin())<<endl; } else if(flagX){ cout<<1<<endl; cout<<*(sY.begin())<<endl; } else{ bool tag=0; int ans; for(set<ll>::iterator it=sX.begin();it!=sX.end();it++){ if(sY.find(*it)!=sY.end()){ tag=1;ans=*it; break; } } if(tag){ cout<<1<<endl; cout<<ans<<endl; } else{ cout<<2<<endl; cout<<*(sX.begin())<<" "<<*(sY.begin())<<endl; } } } } return 0; }
C题:
比赛的最后时刻想到了dp,不过没有写下去,其实就是dp!
关键还是找转态转移。
dp+前缀和优化。
这个可以单独写个题解:
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int INF=(1<<29); const int maxn=5010; const ll p=1000000007; ll n,a,b,k; ll dp[maxn][maxn]; ll sum[maxn]; int main() { freopen("in.txt","r",stdin); while(cin>>n>>a>>b>>k){ MS0(dp); for(int i=1;i<=n;i++) dp[1][i]=(abs(a-i)<abs(a-b))&&(i!=a); sum[0]=0; for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+dp[1][i])%p; for(int i=2;i<=k;i++){ for(int j=1;j<=n;j++){ if(j<b){ dp[i][j]=(dp[i][j]+(sum[(b+j-1)/2]-sum[0]-(sum[j]-sum[j-1])+3*p)%p)%p; } else if(j>b){ dp[i][j]=(dp[i][j]+(sum[n]-sum[(b+j)/2]-(sum[j]-sum[j-1])+3*p)%p)%p; } } for(int j=1;j<=n;j++){ sum[j]=(sum[j-1]+dp[i][j]%p)%p; } } ll ans=0; for(int i=1;i<=n;i++){ ans=(ans%p+dp[k][i]%p)%p; } cout<<ans<<endl; } return 0; }
这场轻松过了AB,要是打那次的div2,过CD上紫?
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1000100; const int INF=(1<<29); ll n,k; int main() { freopen("in.txt","r",stdin); while(cin>>n>>k){ int a=1; for(int i=1;i<=n-k;i++){ printf("%d ",a++); } int c=n,d=a; for(int i=1;i<=k;i++){ if(i&1) printf("%d ",c--); else printf("%d ",d++); } puts(""); } return 0; }
B题:线段树。居然没有看出是线段树。。主要是位运算的问题,位运算和一般的增减还是不一样的。
#include<bits/stdc++.h> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int maxn=1000100; typedef long long ll; int N,Q; ll val[maxn<<2]; struct Query { int l,r; ll ask; };Query q[maxn]; void push_up(int rt) { val[rt]=val[rt<<1]&val[rt<<1|1]; } void push_down(int rt) { val[rt<<1]|=val[rt]; val[rt<<1|1]|=val[rt]; } void update(int L,int R,ll c,int l,int r,int rt) { if(L<=l&&r<=R){ val[rt]|=c; return; } push_down(rt); int m=(l+r)>>1; if(L<=m) update(L,R,c,lson); if(R>m) update(L,R,c,rson); push_up(rt); } ll query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) return val[rt]; push_down(rt); int m=(l+r)>>1; ll res=(1LL<<60)-1; if(L<=m) res&=query(L,R,lson); if(R>m) res&=query(L,R,rson); push_up(rt); return res; } void get(int l,int r,int rt) { if(l==r){ printf("%I64d ",val[rt]); return; } push_down(rt); int m=(l+r)>>1; get(lson); get(rson); push_up(rt); } int main() { //freopen("in.txt","r",stdin); while(cin>>N>>Q){ memset(val,0,sizeof(val)); for(int i=1;i<=Q;i++){ scanf("%d%d%I64d",&q[i].l,&q[i].r,&q[i].ask); update(q[i].l,q[i].r,q[i].ask,1,N,1); } bool flag=1; for(int i=1;i<=Q;i++){ if(query(q[i].l,q[i].r,1,N,1)!=q[i].ask){ flag=0;break; } } if(flag){ puts("YES"); get(1,N,1); puts(""); } else puts("NO"); } return 0; }
明天再补。
C题:概率+dp,略涉及字符串。补补补!
div2 B题:水题二分,画个集合的韦恩图容斥一下就好了。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1000100; const int INF=(1<<29); ll c1,c2,x,y; bool check(ll n) { ll px=n/x; ll py=n/y; ll pxy=n/(x*y); return n-px>=c1&&n-py>=c2&&n-pxy>=c1+c2; } ll bin(ll l,ll r) { while(l<r){ ll m=(l+r)>>1; if(check(m)) r=m; else l=m+1; } return r; } int main() { freopen("in.txt","r",stdin); while(cin>>c1>>c2>>x>>y){ ll n=bin(1,1LL<<60); cout<<n<<endl; } return 0; }
虽然20分钟过了A,不过线段树居然没过,没过的原因还是位运算的细节没想清楚,觉得处理起来比较麻烦,就没往线段树方面想了,还是经验问题。C题比较难,过的人有点少,但既然是dp,又涉及字符串,还是得补,增加经验。
不过要是做了那一场的div2,40min3题?可能也会紫?如果做出线段树呢?
明天补完BC,有时间再开一场,没时间就准备下午的组队赛了。
感觉该补线段树了
====================
做了一些线段树离线的题,补了Orz_panda的A题(线段树离线)和I题(状压dp),回来刷cf了。
第四场:
A题:水题。求[l,r]中二进制表示出现的1的个数最多的数,如果多种答案,输出最小的。
本来以为是数位dp,想了一下原来是个水题,比赛的时候用字符串模拟,51min才过,其实直接用数字二进制弄更简单些。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=120; int n; ll l,r; int numL[maxn],numR[maxn]; int nL,nR; int main() { // freopen("in.txt","r",stdin); cin>>n; while(n--){ scanf("%I64d%I64d",&l,&r); int tmp[maxn]; ll tl=l,tr=r; nL=nR=0; while(tl){ tmp[nL++]=tl%2; tl>>=1; } for(int i=0;i<nL;i++) numL[i]=tmp[nL-1-i]; //cout<<tr<<endl; while(tr){ tmp[nR++]=tr%2; tr>>=1; } for(int i=0;i<nR;i++) numR[i]=tmp[nR-i-1]; // cout<<nL<<" "<<nR<<endl; bool flag=1; for(int i=0;i<nR;i++){ if(!numR[i]){ flag=0;break; } } //cout<<flag<<endl; //for(int i=0;i<nR;i++) cout<<numR[i]<<" ";cout<<endl; if(flag){ printf("%I64d ",r); continue; } //cout<<nL<<" "<<nR<<endl; if(nL<nR){ //cout<<nL<<" "<<((1LL<<nL)-1)<<endl; printf("%I64d ",(1LL<<(nR-1))-1); continue; } int ans[maxn]; memset(ans,0,sizeof(ans)); bool tag=0; for(int i=0;i<nR;i++){ if(tag){ ans[i]=1;continue; } if(numL[i]!=numR[i]){ tag=1; bool tag2=0; for(int j=i+1;j<nR;j++){ if(numR[j]==0){ tag2=1;break; } } if(tag2) ans[i]=0; else ans[i]=1; continue; } ans[i]=numL[i]; } ll Ans=0; // cout<<"f"<<endl; for(int i=0;i<nR;i++){ if(ans[i]) Ans+=(1LL<<(nR-i-1)); } printf("%I64d ",Ans); } return 0; }
B题:这题其实也不难。。
给一个序列,找一对(ai,aj)使得ai>aj,且ai%aj最大。
先排序,然后遍历a[j]*k,k=2,3,....,查找第一个比a[j]*k小的,取余数更新答案。
复杂度 : a[j]*k <= Max,因此这部分复杂度类似素数筛(n/1+n/2+n/3+...+n/1=logn),后面二分查找也是logn,总复杂度(n*logn*logn).
#include<bits/stdc++.h> using namespace std; const int maxn=1000100; const int INF=(1<<29); int n; int a[maxn]; int bin(int l,int r,int x,int mod) { while(l<=r){ int m=(l+r)>>1; // cout<<l<<" "<<r<<" "<<m<<" "<<a[m]<<endl; if((m==n&&a[m]<x)||(a[m]<x&&a[m+1]>=x)) return a[m]%mod; if(a[m]>=x) r=m-1; else l=m+1; } return 0; } int main() { while(cin>>n){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); n=unique(a+1,a+n+1)-(a+1); int Max=a[n]; int ans=0; for(int i=1;i<=n;i++){ for(int j=2;a[i]*(j-1)<=Max;j++){ // cout<<i+1<<" "<<n<<" "<<a[i]*j<<" "<<a[i]<<endl; ans=max(bin(i+1,n,a[i]*j,a[i]),ans); } } cout<<ans<<endl; } return 0; }
第五场:(#273 div 2)
A题:略。
B题:略。
C题:
给定a个红球,b个白球,c个蓝球,求每次选择确定的三个装饰桌子,不能选完全相同的三个,最多能装饰多少桌子。
不妨设a>=b>=c,当a<=(b+c)*2时,直接除以3。
#include<bits/stdc++.h> using namespace std; const int maxn=1000100; const int INF=(1<<29); typedef long long ll; ll r,b,g; void solve() { ll a[3]={r,b,g}; sort(a,a+3); r=a[2];b=a[1];g=a[0]; if(r>(b+g)*2) r=(b+g)*2; cout<<(r+b+g)/3<<endl; } int main() { while(cin>>r>>b>>g){ solve(); } return 0; }
D题:
dp题。http://codeforces.com/contest/478/problem/D
#include<bits/stdc++.h> using namespace std; const int maxn=200100; const int INF=(1<<29); typedef long long ll; const ll MOD=1000000007; ll dp[2][maxn]; int r,g; int main() { while(~scanf("%d%d",&r,&g)){ memset(dp,0,sizeof(dp)); dp[1][0]=r?1:0; dp[1][1]=g?1:0; ll H=0; for(int x=1;;x++){ if((r+g)-x*(x+1)/2>=0) H=x; else break; } ll sum=1; for(int i=2;i<=H;i++){ sum+=i; for(int j=0;j<=g&&j<=sum;j++){ dp[i%2][j]=0; if(sum-j<=r){ dp[i%2][j]=(dp[i%2][j]+dp[(i+1)%2][j])%MOD; if(j>=i) dp[i%2][j]=(dp[i%2][j]+dp[(i+1)%2][j-i])%MOD; } } } ll ans=0; for(int i=0;i<=g;i++) ans=(ans+dp[H%2][i])%MOD; cout<<ans<<endl; } return 0; }
E题:数位dp,待补。。