20160405:
1、bzoj1306 http://www.lydsy.com/JudgeOnline/problem.php?id=1306
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 using namespace std; 6 7 int s2[10],ans[10],tot,n,sz; 8 void dfs(int x,int y,int z){ 9 if(z==sz){inc(i,1,n)if(s2[i]!=ans[i])return; tot++; return;}if(x>n||y>n)return; 10 if(y!=n||(y==n&&ans[x]-s2[x]==3)){ 11 s2[x]+=3; 12 if(s2[x]+3*(n-y)>=ans[x]&&s2[y]+3*(n-x)>=ans[y]&&s2[x]<=ans[x]&&s2[y]<=ans[y])y==n?dfs(x+1,x+2,z+1):dfs(x,y+1,z+1); 13 s2[x]-=3; 14 } 15 if(y!=n||(y==n&&ans[x]-s2[x]==1)){ 16 s2[x]++; s2[y]++; 17 if(s2[x]+3*(n-y)>=ans[x]&&s2[y]+3*(n-x)>=ans[y]&&s2[x]<=ans[x]&&s2[y]<=ans[y])y==n?dfs(x+1,x+2,z+1):dfs(x,y+1,z+1); 18 s2[x]--; s2[y]--; 19 } 20 if(ans[x]==s2[x]||y!=n){ 21 s2[y]+=3; 22 if(s2[x]+3*(n-y)>=ans[x]&&s2[y]+3*(n-x)>=ans[y]&&s2[x]<=ans[x]&&s2[y]<=ans[y])y==n?dfs(x+1,x+2,z+1):dfs(x,y+1,z+1); 23 s2[y]-=3; 24 } 25 } 26 int main(){ 27 scanf("%d",&n); inc(i,1,n)scanf("%d",&ans[i]); 28 memset(s2,0,sizeof(s2)); tot=0; sz=(n*n-n)>>1; 29 dfs(1,2,0); printf("%d",tot); 30 }
题解:爆搜,加入各种奇怪剪枝,比如:剩下的比赛全赢分数都不到要求就返回、当前分数超过了要求……还有一个重要的就是如果当前已经是最后一场就直接算出比赛结果,这个剪枝虽然表面上没什么用但实际上可以把程序从TLE的边缘拯救回来。这种题对我这种从不鸟常数的就是灾难。
20160406:
2、bzoj2662 http://www.lydsy.com/JudgeOnline/problem.php?id=2662
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <queue> 5 #define INF 0x3fffffff 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 #define turn(x,y) (y-1)*n+x 8 using namespace std; 9 10 struct e{int t,w,n;}; int ess,g[100]; e es[200000]; 11 void pe(int f,int t,int w){es[++ess]=(e){t,w,g[f]}; g[f]=ess;} 12 int n,m,k,s,t,d[100][100]; bool inq[100][100]; 13 struct nd{int x,y;}; queue <nd> q; 14 void spfa(){ 15 while(! q.empty())q.pop(); inc(i,1,n)inc(j,1,k+1)d[i][j]=INF; 16 d[1][1]=0; q.push((nd){1,1}); inq[1][1]=1; 17 while(! q.empty()){ 18 nd x=q.front(); q.pop(); inq[x.x][x.y]=0; 19 for(int i=g[x.x];i;i=es[i].n)if(d[es[i].t][x.y]>d[x.x][x.y]+es[i].w){ 20 d[es[i].t][x.y]=d[x.x][x.y]+es[i].w; 21 if(! inq[es[i].t][x.y])q.push((nd){es[i].t,x.y}),inq[es[i].t][x.y]=1; 22 } 23 if(x.y<=k)for(int i=g[x.x];i;i=es[i].n)if(d[es[i].t][x.y+1]>d[x.x][x.y]+(es[i].w>>1)){ 24 d[es[i].t][x.y+1]=d[x.x][x.y]+(es[i].w>>1); 25 if(! inq[es[i].t][x.y+1])q.push((nd){es[i].t,x.y+1}),inq[es[i].t][x.y+1]=1; 26 } 27 } 28 } 29 int main(){ 30 scanf("%d%d%d",&n,&m,&k); ess=0; memset(g,0,sizeof(g)); 31 inc(i,1,m){ 32 int a,b,c;scanf("%d%d%d",&a,&b,&c); pe(a,b,c),pe(b,a,c); 33 } 34 spfa(); int min=INF; inc(i,1,k+1)if(d[n][i]<min)min=d[n][i]; 35 printf("%d",min); 36 return 0; 37 }
题解:分层图最短路。这道题数据弱,可以按次数拆点,但下一道题就不行了,正解是在作spfa时“拆点”,把d弄成二维数组。虽然感觉上是一样的,但时间却相差很大,不知道为什么。
3、bzoj2763 http://www.lydsy.com/JudgeOnline/problem.php?id=2763
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <queue> 5 #define INF 0x3fffffff 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 #define turn(x,y) (y-1)*n+x 8 using namespace std; 9 10 struct e{int t,w,n;}; int ess,g[15000]; e es[200000]; 11 void pe(int f,int t,int w){es[++ess]=(e){t,w,g[f]}; g[f]=ess;} 12 int n,m,k,s,t,d[15000][11]; bool inq[15000][11]; 13 struct nd{int x,y;}; queue <nd> q; 14 void spfa(){ 15 while(! q.empty())q.pop(); inc(i,0,n-1)inc(j,1,k+1)d[i][j]=INF; 16 d[s][1]=0; q.push((nd){s,1}); inq[s][1]=1; 17 while(! q.empty()){ 18 nd x=q.front(); q.pop(); inq[x.x][x.y]=0; 19 for(int i=g[x.x];i;i=es[i].n)if(d[es[i].t][x.y]>d[x.x][x.y]+es[i].w){ 20 d[es[i].t][x.y]=d[x.x][x.y]+es[i].w; if(! inq[es[i].t][x.y])q.push((nd){es[i].t,x.y}),inq[es[i].t][x.y]=1; 21 } 22 if(x.y<=k)for(int i=g[x.x];i;i=es[i].n)if(d[es[i].t][x.y+1]>d[x.x][x.y]){ 23 d[es[i].t][x.y+1]=d[x.x][x.y]; if(! inq[es[i].t][x.y+1])q.push((nd){es[i].t,x.y+1}),inq[es[i].t][x.y+1]=1; 24 } 25 } 26 } 27 int main(){ 28 scanf("%d%d%d%d%d",&n,&m,&k,&s,&t); ess=0; memset(g,0,sizeof(g)); 29 inc(i,1,m){ 30 int a,b,c;scanf("%d%d%d",&a,&b,&c); pe(a,b,c),pe(b,a,c); 31 } 32 spfa(); int min=INF; 33 inc(i,1,k+1)if(d[t][i]<min)min=d[t][i]; 34 printf("%d",min); 35 return 0; 36 }
题解:同上,但这次乱搞会TLE,本弱太弱了!
4、bzoj2038 http://www.lydsy.com/JudgeOnline/problem.php?id=2038
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #define ll long long 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 using namespace std; 8 9 struct nd1{ 10 int l,pl,r,id; ll ans; 11 }; 12 bool cmp1(nd1 a,nd1 b){ 13 if(a.pl!=b.pl)return a.pl<b.pl; if(a.r!=b.r)return a.r<b.r; 14 return a.l<b.l; 15 } 16 bool cmp2(nd1 a,nd1 b){ 17 return a.id<b.id; 18 } 19 nd1 a1[100000];int col[100000],pos[100000],n,m,l,r;ll ans,s[100000]; 20 inline void update(int x,int y){ 21 ans-=(s[col[x]]*(s[col[x]]-1));s[col[x]]+=(ll)y;ans+=(s[col[x]]*(s[col[x]]-1)); 22 } 23 ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} 24 int main(){ 25 scanf("%d%d",&n,&m); inc(i,1,n)scanf("%d",&col[i]); int sz=(int)sqrt(n); 26 inc(i,1,n)pos[i]=(i-1)/sz+1; 27 inc(i,1,m){ 28 int a,b;scanf("%d%d",&a,&b);a1[i]=(nd1){a,pos[a],b,i,0}; 29 } 30 sort(a1+1,a1+1+m,cmp1); l=1; r=0; ans=0; memset(s,0,sizeof(s)); 31 inc(i,1,m){ 32 while(r<a1[i].r)update(r+1,1),r++; 33 while(l>a1[i].l)update(l-1,1),l--; 34 while(r>a1[i].r)update(r,-1),r--; 35 while(l<a1[i].l)update(l,-1),l++; 36 a1[i].ans=ans; 37 } 38 sort(a1+1,a1+1+m,cmp2); 39 inc(i,1,m){ 40 if(a1[i].ans==0)printf("0/1\n");else{ 41 ll a2=gcd(a1[i].ans,(ll)(a1[i].r-a1[i].l+1)*(a1[i].r-a1[i].l)); 42 printf("%lld/%lld\n",a1[i].ans/a2,(ll)(a1[i].r-a1[i].l+1)*(a1[i].r-a1[i].l)/a2); 43 } 44 } 45 return 0; 46 }
题解:不知道要用什么数据结构,但是可以用一个全局的数组保存每个颜色当前数量,使由区间[l,r]推出[l,r±1]的答案和[l±1,r]的复杂度为O(1),对这种问题,可以用复杂度为O(nsqrt(n))的莫队算法解决。
莫队算法是一种离线算法,将询问按某种顺序排序,使得均摊复杂度为O(nsqrt(n)),那怎么排序呢?如果按左端点排序,那么r将有可能多次大幅度摆动,使复杂度退化成O(n^2),正解是对端点分块,让后按左端点所在块为第一关键字排序,右端点为第二关键字排序。这样子当两个询问l在同一块时,l只有可能移sqrt(n)次。l在同一块的多次询问q只能右移n次,l在不同块时r可能左移n次,但因为只有sqrt(n)块,所以需移n次的操作都只有sqrt(n)次,因此均摊复杂度是O(sqrt(n))。所有的均摊复杂度都是玄学……
因为中间结果没有强制转化成long long,wa了好几发,本弱太弱了!
20160407:
5、bzoj3190 http://www.lydsy.com/JudgeOnline/problem.php?id=3190
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define eps 1e-8 6 using namespace std; 7 8 struct nd{ 9 int s,v,id; 10 bool operator < (const nd& a)const{ 11 if(v!=a.v)return v<a.v; return s<a.s; 12 } 13 }; 14 double solve(nd a,nd b){ 15 return (double)(a.s-b.s)/(double)(b.v-a.v); 16 } 17 nd nds[20000],s[20000]; int ans[20000],n,tp; 18 int main(){ 19 scanf("%d",&n); inc(i,1,n)scanf("%d",&nds[i].s),nds[i].id=i; inc(i,1,n)scanf("%d",&nds[i].v); sort(nds+1,nds+1+n); 20 tp=1;s[tp]=nds[1];ans[tp]=nds[1].id; 21 inc(i,2,n){ 22 while(tp>=1&&solve(s[tp],nds[i])<-eps)tp--; 23 while(tp>=2&&solve(s[tp-1],s[tp])>solve(s[tp],nds[i]))tp--; 24 s[++tp]=nds[i]; ans[tp]=nds[i].id; 25 } 26 sort(ans+1,ans+1+tp); printf("%d\n",tp); 27 inc(i,1,tp){ 28 i==tp?printf("%d",ans[i]):printf("%d ",ans[i]); 29 } 30 return 0; 31 }
题解:有人说是类似线性规划,用半平面交,反正我不会,数学考试是线性规划也错得一塌糊涂QAQ。
黄学长用的是维护斜率的方法,按照斜率排个序,依次插入一个单调栈,如果栈顶不满足XX条件(说不清楚,画个图)就弹掉,最后留在栈里的就是答案。
6、bzoj1007 http://www.lydsy.com/JudgeOnline/problem.php?id=1007
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cmath> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define eps 1e-8 7 using namespace std; 8 9 struct nd{ 10 int a,b,id; 11 bool operator < (const nd& x)const{ 12 if(a!=x.a)return a<x.a; return b<x.b; 13 } 14 }; 15 double solve(nd a,nd b){ 16 return (double)(a.b-b.b)/(double)(b.a-a.a); 17 } 18 nd nds[100000],s[100000]; int ans[100000],n,tp; 19 int main(){ 20 scanf("%d",&n);inc(i,1,n)scanf("%d",&nds[i].a),scanf("%d",&nds[i].b),nds[i].id=i; sort(nds+1,nds+1+n); 21 tp=1;s[tp]=nds[1];ans[tp]=nds[1].id; 22 inc(i,2,n){ 23 if(nds[i].a-s[tp].a==0)tp--; 24 while(tp>=2&&solve(s[tp-1],s[tp])>=solve(s[tp],nds[i]))tp--; 25 s[++tp]=nds[i]; ans[tp]=nds[i].id; 26 } 27 sort(ans+1,ans+1+tp);inc(i,1,tp)printf("%d ",ans[i]); 28 return 0; 29 }
题解:和上一道差不多,但是因为是比较随意的直线,所以还要多一些判断条件。
20160408:
7、bzoj3289 http://www.lydsy.com/JudgeOnline/problem.php?id=3289
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cmath> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define lowbit(a) a&(-a) 7 #define ll long long 8 using namespace std; 9 10 struct nd1{int x,y;}; 11 bool cmp1(nd1 a,nd1 b){return a.x<b.x;} 12 struct nd2{int l,pl,r; ll ans; int id;}; 13 bool cmp2(nd2 a,nd2 b){if(a.pl!=b.pl)return a.pl<b.pl; if(a.r!=b.r)return a.r<b.r; return a.l<b.l;} 14 bool cmp3(nd2 a,nd2 b){return a.id<b.id;} 15 ll c[100000],ans,l,r; int n,q,ls[100000],pos[100000]; nd1 f[100000]; nd2 ask[100000]; 16 inline void add(int x,ll y){while(x<=n)c[x]+=y,x+=lowbit(x);} 17 inline ll query(int x){ll qy=0; while(x>=1)qy+=c[x],x-=lowbit(x); return qy;} 18 int main(){ 19 scanf("%d",&n); inc(i,1,n)scanf("%d",&f[i].x),f[i].y=i; sort(f+1,f+n+1,cmp1); 20 inc(i,1,n)ls[f[i].y]=i; int sz=(int)sqrt(n); inc(i,1,n)pos[i]=(i-1)/sz+1; 21 scanf("%d",&q); inc(i,1,q){int a,b; scanf("%d%d",&a,&b); ask[i]=(nd2){a,pos[a],b,0,i};} 22 sort(ask+1,ask+1+q,cmp2); memset(c,0,sizeof(c)); ans=0; l=1; r=0; 23 inc(i,1,q){ 24 while(r<ask[i].r){int x=r+1; x=n-ls[x]; ll a1=query(x); ans+=a1; add(x+1,1); r++;} 25 while(l>ask[i].l){int x=l-1; x=n-ls[x]; ll a1=(r-l+1)-query(x); ans+=a1; add(x+1,1); l--;} 26 while(r>ask[i].r){int x=r; x=n-ls[x]; ll a1=query(x); ans-=a1; add(x+1,-1); r--;} 27 while(l<ask[i].l){int x=l; x=n-ls[x]; ll a1=(r-l)-query(x); ans-=a1; add(x+1,-1); l++;} 28 ask[i].ans=ans; 29 } 30 sort(ask+1,ask+1+q,cmp3); inc(i,1,q)printf("%lld\n",ask[i].ans); 31 return 0; 32 }
题解:还是莫队,但是转移的时候用树状数组维护逆序对个数,总复杂度为O(nsqrt(n)log2n)。
因为是从大到小插入的,所以维护时要用r-l+1减。
8、bzoj2456 http://www.lydsy.com/JudgeOnline/problem.php?id=2456
1 #include <cstdio> 2 using namespace std; 3 int main(){ 4 int n,x,t,tot; tot=0; scanf("%d",&n); 5 while(n--){ 6 scanf("%d",&x); 7 if(tot==0)t=x,tot=1;else if(x==t)tot++;else tot--; 8 } 9 printf("%d",t); 10 }
题解:注意空间只有1M,显然不能开数组。用两个变量,一个存“当前数”,另一个存“当前数”的个数,如果读入的数与“当前数”相同就个数加一,如果不同就减一。如果个数减到0就换“当前数”为现在读入的数。因为如果那个“众数”个数超过ndiv2,所以一定不会被其他数抵消完,能够“坚持”到最后,所以答案就是最后的那个“当前数”。思路真巧。