Codeforces Round #345 (Div. 2)
居然有场中国下午场次的CF,猴开心啊,于是就报名了...
结果被虐傻了...果然不能太激动啊...
A题题目翻译挂了,少了一个无解的情况,导致机房前20Min没有一个人能过A题...直到后面有高人相助才过,好惨
C题前面都开了long long,只有输出没开I64d,结果就丢了1206分...这什么鬼啊...
D题敲了20min的程序结果挂了,于是删掉重打,打到要结束了才发现题目两个东西弄反了,然后仓促改了一改...呼,然后少改一个地方,然后一堆细节的东西也有问题...表示无奈
A题:两个电池,每秒钟充电的话电量+1,不充电-2,一个<=0,就结束。问最多撑多久?
感觉贪心就好,就是每次给电量小的+1,结果WA了!好吧,我觉得是我贪心可能有问题,于是我就打了一个大暴力,f[i][j]表示两个电池的状态,WA了!然后我放弃了,我吃饭去了...回来就有人说还有一句话你没看见。
如果一个电池的电量=1,你必须给它充电。结果1 1输出是0...好坑啊...
O(100^3)大暴力:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int f[210][110][110]; int main(){ #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); #endif int a1,a2,t=0; scanf("%d%d",&a1,&a2); if(a1==1 && a2==1){ printf("0");return 0; } f[0][a1][a2]=1; f[0][a2][a1]=1; for(int i=0;i<=200;i++) for(int j=1;j<=102;j++) for(int k=1;k<=102;k++) if(f[i][j][k]){ if(j>2) f[i+1][j-2][k+1]=1; if(k>2) f[i+1][j+1][k-2]=1; } for(int i=200;i>=0;i--){ for(int j=1;j<=100;j++) for(int k=1;k<=100;k++) if(f[i][j][k]){ printf("%d",i+1);return 0; } } return 0; }
O(100)的贪心,也可以过。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main(){ #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); #endif int a,b,t=0; scanf("%d%d",&a,&b); if(a==1 && b==1){ printf("0");return 0; } while(a>0 && b>0){ if(a>b) swap(a,b); a++,b-=2;t++; } printf("%d",t); return 0; }
B题:给你n个序列,然后让你找到一种排列方式,使得ai<ai+1的数目最多。n<=1000,ai<=1000
好吧,这题当时只想到暴力,唔暴力就是1000*1000,如果这个值有,那么就接在前面的放上这个。最后在得到的序列中暴力算一下。
然后考完,唔,zyj说这题可以直接n-出现次数最多的元素,好神啊...相当于最小链覆盖=最长反链长度[我怎么会想到导弹拦截去...2333],如果还没理解这个做法的话,就可以脑补一下,发现除了这个出现次数最多的,其它的都可以找到贡献[可以往前也可以往后找贡献]。
O(1000^2)大暴力:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1010; int n,k,ans; int a[maxn],b[maxn]; int T[maxn]; int main(){ #ifndef ONLINE_JUDGE freopen("B.in","r",stdin); #endif scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),T[a[i]]++; for(int i=1;i<=1000;i++) for(int j=1;j<=1000;j++){ if(T[j]) T[j]--,b[++k]=j; } for(int i=1;i<=k;i++) if(b[i]<b[i+1]) ans++; printf("%d",ans); return 0; }
O(1000)的神奇做法
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1010; int n,Max; int a[maxn],T[maxn]; int main(){ #ifndef ONLINE_JUDGE freopen("B.in","r",stdin); #endif scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]),T[a[i]]++; Max=max(Max,T[a[i]]); } printf("%d",n-Max); return 0; }
C题:给你n个点,求有多少点对的欧几里得距离=曼哈顿距离
这就是问有多少点对在同一行或者同一列,然后*C(这一行/列上点数目,2)就是这一行/列上点对的数量。
然后还要删掉重合的点的贡献,考试时最后一步没有输出long long导致崩盘。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=200010; typedef long long ll; struct Point{ int x,y; }s[maxn]; bool cmp(const Point &A,const Point &B){ return A.x<B.x; } bool cmp2(const Point &A,const Point &B){ return A.y<B.y; } bool cmp3(const Point &A,const Point &B){ if(A.x!=B.x) return A.x<B.x; return A.y<B.y; } int n; ll ans; int main(){ #ifndef ONLINE_JUDGE freopen("C.in","r",stdin); #endif int cnt; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&s[i].x,&s[i].y); sort(s+1,s+n+1,cmp); s[n+1].x=s[n+1].y=-1e9; for(int i=1;i<=n;i++){ cnt=1; while(s[i].x==s[i+1].x) cnt++,i++; ans+=(ll)cnt*(cnt-1)/2; } sort(s+1,s+n+1,cmp2); for(int i=1;i<=n;i++){ cnt=1; while(s[i].y==s[i+1].y) cnt++,i++; ans+=(ll)cnt*(cnt-1)/2; } sort(s+1,s+n+1,cmp3); for(int i=1;i<=n;i++){ cnt=1; while(s[i].y==s[i+1].y && s[i].x==s[i+1].x) cnt++,i++; ans-=(ll)cnt*(cnt-1)/2; } printf("%I64d",ans); return 0; }
D题:有n张图片,你可以向左或者向右滑,每次滑需要a秒,看一张横着的图片需要1秒,一张竖着的图片需要1+b秒。如果现在看的图片之前没有看过,那么必须花时间看完它;如果已经看过,可以不花时间在看它上。你一开始在1号图片,问T秒最多看多少图片。
根据题目信息,这题应该是看一段前缀+一段后缀,那么通过类似贪心的想法来找到最优的值。
首先贪心找到后缀最远的,同时尝试往前拓展找到这个后缀对应的最远的前缀。然后每次后缀指针往后挪一个,前面的指针跟着动,然后更新答案就行了。而我可以通过比较前缀和后缀的长度来确定一开始翻的方向。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=2000010; typedef long long ll; int n,l,r,a,b,T,ans; ll s1[maxn],s2[maxn]; char ch[maxn]; int main(){ #ifndef ONLINE_JUDGE freopen("D.in","r",stdin); #endif scanf("%d%d%d%d",&n,&a,&b,&T); scanf("%s",ch+1); if(ch[1]=='w') T-=1+b; else T-=1; if(T<0){printf("0");return 0;} ans=1;l=1;r=n+1; for(int i=2;i<=n;i++) if(ch[i]=='w') s1[i]=s1[i-1]+1+b; else s1[i]=s1[i-1]+1; for(int i=n;i>=2;i--){ if(ch[i]=='w') s2[i]=s2[i+1]+1+b; else s2[i]=s2[i+1]+1; if(T>=s2[i]+(n-i+1)*a) r=i; } if(r==2){printf("%d",n);return 0;} for(int i=2;i<=n;i++) if(i-1>(n-r+1)){ if(T>=s1[i]+s2[r]+((n-r+1)<<1)*a+(i-1)*a) l=i; else break; } else{ if(T>=s1[i]+s2[r]+((i-1)<<1)*a+(n-r+1)*a) l=i; else break; } ans=l+(n-r+1); int t; while(r<=n){ r++; while(l<n){ if(l>(n-r+1)){ if(T>=s1[l+1]+s2[r]+((ll)(n-r+1)<<1)*a+l*a) l++; else break; } else{ if(T>=s1[l+1]+s2[r]+((ll)l<<1)*a+(n-r+1)*a) l++; else break; } } ans=max(ans,l+n-r+1); } printf("%d",ans); return 0; }
E题:有个n*m的矩阵要求你将这个矩阵压缩成一个n*m的矩阵,但是要保证每行每列上的权值的相对大小不变。保证相对大小不变的基础上要求最大值最小,求这个压缩后的矩阵。
我们可以首先将所有元素排一个序,然后每次取出最小的那个,一开始显然标号是1,然后之后的就看和这一行这一列上的关系,如果比这行/列上的元素大,那么就取这行/列上的元素+1;当然对于行列也要取最大值。
然后因为相等的关系也要保证,所以我们可以用并查集把同一行同一列的相同元素连起来,然后定根的时候,统一取大的作为根,那么查询的时候就会查到这个元素的值了。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1000010; inline int in(){ int x=0;char ch=getchar(); while(ch>'9' || ch<'0') ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } struct Node{ int x,y,v; Node(){} Node(int a,int b,int c){x=a,y=b,v=c;} }s[maxn]; int n,m,tot; int a[maxn],b[maxn]; int row_mx[maxn],cur_mx[maxn]; int p[maxn]; bool cmp(const Node &A,const Node &B){ return A.v<B.v; } inline int Find(int x){ int r=x,pre; while(r!=p[r]) r=p[r]; while(x!=r){ pre=p[x],p[x]=r,x=pre; } return r; } int main(){ #ifndef ONLINE_JUDGE freopen("E.in","r",stdin); #endif n=in();m=in(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[++tot]=in(),s[tot]=Node(i,j,a[tot]); sort(s+1,s+tot+1,cmp); for(int i=1;i<=tot;i++) p[i]=i; for(int i=1;i<=tot;i++){ int x=s[i].x,y=s[i].y,v=s[i].v; int id=(x-1)*m+y,t1=0,t2=0; int fx=Find(row_mx[x]),fy=Find(cur_mx[y]); if(v>a[fx]) t1++; if(v>a[fy]) t2++; b[id]=max(b[fx]+t1,b[fy]+t2); if(v==a[fx]){ int f=Find(id); if(b[f]>b[fx]) p[fx]=f; else p[f]=fx; } if(v==a[fy]){ int f=Find(id); if(b[f]>b[fy]) p[fy]=f; else p[f]=fy; } row_mx[x]=cur_mx[y]=Find(id); } int ip=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) printf("%d ",b[Find(++ip)]); putchar('\n'); } return 0; }