【SRM15】题解
恭喜TJM和CYC成功ak!!!%%%
终于又补完一次SRM的题啦蒟蒻选手终于可以发题解辣~
蒟蒻选手比赛经过(dalao可以直接跳过这段):
8:30比赛开始
咦第一题,模拟?dp?
再看一眼,靠,裸的线段覆盖,贪心啊。十分钟码完调完(really?),8:57提交,过!
t2是什么鬼?网络流?好像有点像啊(误,可是怎么建图啊(接着挣扎了半个小时),诶好像可以这么建,试一试!
五分钟码完调,诶怎么回事总是输出n*A?样例都过不了啊qvq。
再一看建的图,我去这建图方式从头错到尾啊。
完,陷入僵局。
心灰意冷地再看一眼,woc,sb最小生成树,还是模板......弱智地赶紧码完过样例就交。此时10:08。
t3是什么东西(弱智++),想起了NOIP2005的篝火晚会?(事实证明两者毫无关系)啊环形的题目我最怕了QAQ,于是各种脑洞大开,什么树状数组、环形dp想了个遍,挣扎一个多小时最后三分钟交了个n方暴力就跑...
预测分数:100+100+30=230
实际分数:60+40+30=130
啊啊啊t1和t2怎么都挂了QAQ。然后就陷入了艰苦卓绝的找错误阶段——
最后发现,t1少打了个+1,t2枚举上界和并查集合并集合写错,惨......%>_<%
好的终于水完了,可以开始发题解了...
T1
看出是线段覆盖就没什么难的了,排完序贪一下心就可以了,注意两边区间都是闭的所以+1什么的别打漏...
#include<cstdio> #include<cstring> #include<algorithm> #define mem(a) memset(a,0,sizeof(a)) typedef long long LL; const int N=5e5+7; using namespace std; int n; struct node{ int a,b; }e[N]; int read() { int ans=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f; } bool cmp(node c,node d){return c.a<d.a||(c.a==d.a&&c.b>d.b);} int main() { n=read();int ai; for(int i=1;i<=n;i++){ ai=read(); e[i].a=max(1,i-ai); e[i].b=min(n,i+ai); } sort(e+1,e+1+n,cmp); int sum=1,x=e[1].b; for(int i=1;i<=n;i++){ if(x==n)break; if(e[i].a>x){ sum++; x=e[i].b; continue; } int x0=x; for(int j=i;j<=n;j++){ if(e[j].a>x+1)break; if(e[j].b<=x0){i=j;continue;} x0=e[j].b; i=j; } if(x<x0){x=x0;sum++;} } printf("%d",sum); return 0; }
T2
每个顶点先向0连一条权值为A的边,再两两之间连一条权值为B*两点曼哈顿距离的边,排完序跑一次Kruskal就行啦。
#include<cstdio> #include<cstring> #include<algorithm> typedef long long LL; const int N=1e3+5,inf=0x3f3f3f3f; using namespace std; struct node{ int w,from,to; }e[1000100]; int n,s=0,A,B,tot=0; int xi[N],yi[N],fa[N]; int read() { int ans=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f; } void add(int u,int v,int w) { tot++;e[tot].from=u;e[tot].to=v;e[tot].w=w; } bool cmp(node a,node b){return a.w<b.w;} int getf(int x) { if(fa[x]==x)return x; fa[x]=getf(fa[x]); return fa[x]; } int main() { int ans=0; n=read();A=read();B=read(); for(int i=0;i<=n;i++)fa[i]=i; for(int i=1;i<=n;i++){ xi[i]=read();yi[i]=read();fa[i]=i; add(s,i,A); for(int j=1;j<i;j++){ int x=abs(xi[i]-xi[j])+abs(yi[i]-yi[j]); add(i,j,B*x); } } sort(e+1,e+1+tot,cmp); int k=0; for(int i=1;i<=tot;i++){ int x=e[i].from,y=e[i].to; if(getf(x)!=getf(y)){ fa[fa[y]]=fa[x]; ans+=e[i].w; k++; } if(k>=n)break; } printf("%d",ans); return 0; }
T3
比赛时自己是没想出来的,最后是请教cyc dalao才会的(orz cyc><),这里直接甩一波他的题解:
这里我们用b[i]来表示在第i次旋转操作时A变B的si的个数。
注意,每次ans+(A-B)时,我们是默认原来与i=1匹配的那个s[j]现在匹配给了i=0,而实际上此时的s[j]应当匹配i=n,所以ans还要先减去s[j](s[j]-0)再加上n-s[j](s[j]一定小于或等于n)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> typedef long long LL; const int N=2e6+10; using namespace std; int n,si[N],b[N],A=0,B=0; int read() { int ans=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f; } int main() { n=read();LL ans=0; for(int i=1;i<=n;i++){ si[i]=read(); ans+=abs(si[i]-i); if(si[i]<i){ B++; b[i-si[i]]++; } else{ A++; b[i+n-si[i]]++; } } LL anss=ans; for(int i=1;i<=n-1;i++){ anss+=A-B-si[i]+n-si[i]; A--;B++;A+=b[i];B-=b[i]; ans=min(ans,anss); } printf("%lld\n",ans); return 0; }
其实这一场SRM的题目并不难(好吧除了T3),可以说出题人是灰常灰常良心的。然而就是这些基础的东西我都没能做好。如今NOIP2017只剩80天了,如果不想留下遗憾的话,这种事情以后绝对不能再发生了啊!
自己要加油啊!!!