CONTEST36 小Z的模拟赛(2)
A.小Z的可恶路障
题目:http://www.luogu.org/problem/show?pid=U126
题解:暴力也可以过吧。我为了保险先求了一次最短路,然后枚举这条最短路上的所有边。。。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxm 100000 14 #define eps 1e-10 15 #define ll long long 16 #define pa pair<int,int> 17 #define for0(i,n) for(int i=0;i<=(n);i++) 18 #define for1(i,n) for(int i=1;i<=(n);i++) 19 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 20 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 21 #define mod 1000000007 22 using namespace std; 23 inline int read() 24 { 25 int x=0,f=1;char ch=getchar(); 26 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 27 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 28 return x*f; 29 } 30 struct edge{int go,next,w,from;}e[2*maxm]; 31 int n,m,tot=1,q[maxm],d[maxm],head[maxm],pre[maxm]; 32 bool v[maxm],can[maxm]; 33 void ins(int x,int y,int z) 34 { 35 e[++tot].go=y;e[tot].from=x;e[tot].w=z;e[tot].next=head[x];head[x]=tot; 36 } 37 void insert(int x,int y,int z) 38 { 39 ins(x,y,z);ins(y,x,z); 40 } 41 void spfa() 42 { 43 for(int i=1;i<=n;++i) d[i]=inf; 44 memset(v,0,sizeof(v)); 45 int l=0,r=1,x,y;q[1]=1;d[1]=0; 46 while(l!=r) 47 { 48 x=q[++l];if(l==maxm-1)l=0;v[x]=0; 49 for(int i=head[x];i;i=e[i].next) 50 if(d[x]+e[i].w<d[y=e[i].go]) 51 { 52 d[y]=d[x]+e[i].w; 53 pre[y]=i; 54 if(!v[y]){v[y]=1;q[++r]=y;if(r==maxm-1)r=0;} 55 } 56 } 57 } 58 int main() 59 { 60 freopen("input.txt","r",stdin); 61 freopen("output.txt","w",stdout); 62 n=read();m=read();int x,y,z; 63 for1(i,m)x=read(),y=read(),z=read(),insert(x,y,z); 64 spfa(); 65 for(int i=pre[n];i;i=pre[e[i].from])can[i]=1; 66 int ans=0,tmp=d[n]; 67 for1(i,tot) 68 if(can[i]) 69 { 70 e[i].w<<=1; 71 e[i^1].w<<=1; 72 spfa(); 73 ans=max(ans,d[n]-tmp); 74 e[i].w>>=1; 75 e[i^1].w>>=1; 76 } 77 printf("%d\n",ans); 78 return 0; 79 }
B.小Z的游戏分队
题目:http://www.luogu.org/problem/show?pid=U127
题解:这题我做的比较满意,说一下我的做法:
首先i 不愿意与 j 在一组,那么我们就merge(i,j+n) merge(j+n,i) (做过食物链、关押罪犯的都知道什么意思吧。。。)
然后执行完之后整个图就分为了好多个块块,有些块之间有边相连,代表这两块一定不能在同一组里。
当然一个块最多只会和一个块相连,否则这两个块一定已经被缩成了一个块
然后怎么做呢?类似与搭建双塔,我们可以用一个二维可行性背包DP,当然应为总人数是确定的,所以可以缩成1维
状态如何转移呢?
1 f[0]=1; 2 for1(i,n) 3 if(s[i]) 4 { 5 if(can[i]) 6 { 7 for3(j,n-s[i],0) 8 if(f[j])f[j+s[i]]=1; 9 } 10 else if(b[i]) 11 { 12 for3(j,n,0) 13 if(f[j]) 14 { 15 f[j]=0; 16 if(j+s[i]<=n)f[j+s[i]]=1; 17 if(j+s[b[i]]<=n)f[j+s[b[i]]]=1; 18 } 19 } 20 }
应该很清楚了吧。如果 i 块没有边与其他块相连,那么它可以选择加进 a 组,或者不加进 a 组
否则 它与它相连的块要么一个在 a 组,要么 另一个在 a 组。
最后统计一下就ok了。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 100000 14 #define maxm 500+100 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 22 #define mod 1000000007 23 using namespace std; 24 inline int read() 25 { 26 int x=0,f=1;char ch=getchar(); 27 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 28 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 29 return x*f; 30 } 31 int n,fa[maxn],a[maxn],b[maxn],s[maxn]; 32 bool can[maxn],f[maxn]; 33 inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} 34 inline void merge(int x,int y) 35 { 36 int xx=find(x),yy=find(y); 37 if(xx!=yy)fa[yy]=xx; 38 } 39 int main() 40 { 41 freopen("input.txt","r",stdin); 42 freopen("output.txt","w",stdout); 43 n=read(); 44 for1(i,n*2)fa[i]=i; 45 for1(i,n) 46 { 47 int x=read(); 48 while(x){can[x]=1;x=read();}; 49 for1(j,n) 50 { 51 if(!can[j]&&j!=i) 52 { 53 if(find(j)==find(i)||find(j+n)==find(i+n)) 54 {printf("No solution\n");return 0;} 55 merge(i,j+n); 56 merge(j,i+n); 57 } 58 can[j]=0; 59 } 60 } 61 for1(i,n)if(find(i)==find(i+n)){printf("No solution\n");return 0;} 62 for1(i,n)s[find(i)]++; 63 for1(i,n)can[i]=1; 64 for1(i,n) 65 for1(j,n) 66 if(find(i)==find(j+n)) 67 { 68 int x=find(i),y=find(j); 69 b[min(x,y)]=max(x,y); 70 can[x]=can[y]=0; 71 } 72 f[0]=1; 73 for1(i,n) 74 if(s[i]) 75 { 76 if(can[i]) 77 { 78 for3(j,n-s[i],0) 79 if(f[j])f[j+s[i]]=1; 80 } 81 else if(b[i]) 82 { 83 for3(j,n,0) 84 if(f[j]) 85 { 86 f[j]=0; 87 if(j+s[i]<=n)f[j+s[i]]=1; 88 if(j+s[b[i]]<=n)f[j+s[b[i]]]=1; 89 } 90 } 91 } 92 int ans=n,x=0,y=n; 93 for1(i,n) 94 if(f[i]) 95 { 96 int j=n-i; 97 if(abs(i-j)<ans)ans=abs(i-j),x=i,y=j; 98 } 99 printf("%d %d\n",min(x,y),max(x,y)); 100 return 0; 101 }
C.小Z的神奇数列
题目:http://www.luogu.org/problem/show?pid=U129
题解:SXBK的出题人,硬是把我的线段树卡到和暴力一个分T_T
线段树的做法应该是显然的吧,先将数组排序,然后每次二分到要删的位置,线段树单点修改一下。
不知道出题人的标程是什么神奇的做法。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 1000000+10000 14 #define maxm 5000000+10000 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 22 #define mod 317847191 23 using namespace std; 24 inline int read() 25 { 26 int x=0,f=1;char ch=getchar(); 27 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 28 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 29 return x*f; 30 } 31 int n,m,a[maxn],mx[maxm],mi[maxm],ret[maxm]; 32 bool b[maxn]; 33 int find(int x) 34 { 35 int l=1,r=n,mid; 36 while(l<=r) 37 { 38 mid=(l+r)>>1; 39 if(a[mid]>x||(a[mid]==x&&b[mid]))r=mid-1; 40 else l=mid+1; 41 } 42 b[l]=0; 43 return l; 44 } 45 inline void pushup(int k) 46 { 47 mx[k]=max(mx[k<<1],mx[k<<1|1]); 48 mi[k]=min(mi[k<<1],mi[k<<1|1]); 49 ll tmp=ret[k<<1]; 50 tmp=(tmp*ret[k<<1|1])%mod; 51 ret[k]=tmp; 52 } 53 inline void build(int k,int l,int r) 54 { 55 int mid=(l+r)>>1; 56 if(l==r){mx[k]=mi[k]=ret[k]=a[l];return;} 57 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 58 pushup(k); 59 } 60 inline void del(int k,int l,int r,int x) 61 { 62 int mid=(l+r)>>1; 63 if(l==r){mx[k]=-inf;mi[k]=inf;ret[k]=1;return;} 64 if(x<=mid)del(k<<1,l,mid,x);else del(k<<1|1,mid+1,r,x); 65 pushup(k); 66 } 67 int main() 68 { 69 freopen("input.txt","r",stdin); 70 freopen("output.txt","w",stdout); 71 n=read();m=read(); 72 for1(i,n)a[i]=read(); 73 sort(a+1,a+n+1); 74 for1(i,n)b[i]=1; 75 build(1,1,n); 76 ll ans,x,y; 77 while(m--) 78 { 79 char ch[2]; 80 scanf("%s",ch); 81 if(ch[0]=='D')del(1,1,n,find(read())); 82 else if(ch[0]=='B')printf("%d\n",mx[1]); 83 else if(ch[0]=='S')printf("%d\n",mi[1]); 84 else if(ch[0]=='M') 85 { 86 for(ans=1,x=mx[1],y=mi[1];y;y>>=1,x=(x*x)%mod) 87 if(y&1)ans=(ans*x)%mod; 88 printf("%d\n",ans); 89 } 90 else printf("%d\n",ret[1]); 91 } 92 return 0; 93 }
D.小Z的迷之阶梯
题目:http://www.luogu.org/problem/show?pid=U130
题解:出题人的声明就是在告诉我们这是道大水题吧。。。
n=200的数据范围很容易让我们想到n^3的算法,枚举上一个点o(n),枚举从该点向下退的次数o(n),
状态数o(n),n^3水过。。。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 100000 14 #define maxm 500+100 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 22 #define mod 1000000007 23 using namespace std; 24 inline int read() 25 { 26 int x=0,f=1;char ch=getchar(); 27 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 28 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 29 return x*f; 30 } 31 int n,a[maxn],f[maxn]; 32 int main() 33 { 34 freopen("input.txt","r",stdin); 35 freopen("output.txt","w",stdout); 36 n=read(); 37 for1(i,n)a[i]=read(); 38 f[1]=0; 39 for2(i,2,n)f[i]=inf; 40 for2(i,2,n) 41 for1(j,i-1) 42 for0(k,j-1) 43 if(a[j-k]+(1<<k)>=a[i]) 44 f[i]=min(f[i],f[j]+k+1); 45 printf("%d\n",f[n]==inf?-1:f[n]); 46 return 0; 47 }