fkwの题目(祝松松生日快乐!)
麓山国际实验学校
傅少,匡哥和巨夫出的题目(共3道)
一、题目概况
题目名称 |
打地铺 |
泡妹子 |
开房间 |
题目类型 |
传统 |
传统 |
传统 |
可执行文件名 |
deeeep |
soccer |
room |
输入文件名 |
deeeep.in |
soccer.in |
room.in |
输出文件名 |
deeeep.out |
soccer.out |
room.out |
测试点时限 |
4s |
1s |
1s |
内存限制 |
768M |
128M |
128M |
测试点数目 |
10 |
20 |
20 |
测试点分值 |
10 |
5 |
5 |
二、提交源代码文件名
对于Pascal语言 |
deeeep.pas |
soccer.pas |
paint.pas |
对于C 语言 |
deeeep.c |
soccer.c |
paint.c |
对于C++ 语言 |
deeeep.cpp |
soccer.cpp |
paint.cpp |
三、编译命令(不包含任何优化开关)
对于Pascal语言 |
|
|
|
|
对于C 语言 |
-lm |
-lm |
-lm |
-lm |
对于C++ 语言 |
-lm |
-lm |
-lm |
-lm |
四、注意事项
1、 文件夹名、文件名(程序名和输入输出文件名)必须使用英文小写。
2、 C/C++中函数main()的返回值类型必须是int,程序正常结束时的返回值必须是0。
3、 最终测试时,所有编译命令均不打开任何优化开关
4、 今天的题目,除了暴力分,数据都较大,请做好判断,不要卡评测机
一、打地铺(deeeep)
松松生日快乐
[题目描述]
最近LSGJ机房的同学迷上了一个风靡全球的游戏dee....(此处省略一万个e)p,在一个风和日丽的早晨,我们和石室战队发生了冲突。我们的松松为了保住LSGJ机房的荣誉(主要是在他的小迷妹面前出风头),和ji哥一起制定了一个策略,来提高战斗力(S),首先,一共有2n个人分成两队,每次每两队的任意两个人可以组成一队,一共组成n队。然后一队每一个人和另一队每一个人有一个默契度值,称为ai,j。每个人有一个自己喜欢玩的动物,一队每一个人和另一队每一个人有一个动物冲突值,为bi,j。假设每一个队的默契度值为ai′,每一队的动物冲突值为bi′。
总战力怎么算呢?公式就是S=(a1′+a2′+...+an′)/(b1′+b2′+...+bn′)。松松左想右想,硬是没想出解决办法,所以这个问题就交个你了。为了打败石室战队,当然要求S最大。
[输入格式]
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示ai,j。
接下来n行,每行n个整数,第i行第j个数表示bi,j。
[输出格式]
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等。
[输入样例]
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
[输出样例]
5.357143
[说明]
对于10%的数据,1≤n≤5
对于40%的数据,1≤n≤18
另有20%的数据,bi,j≤1
对于100%的数据,1≤n≤100,1≤ai,j,bi,j<=1e4
时间限制为4S;
二、泡妹子(soccer)
松松生日快乐
[题目描述]
松松想泡妹子了,他已经有了目标。于是,松松决定带那个妹子去看球赛。这场比赛,共n支球队,进行m场比赛。
这个妹子答应了,但他是想看帅哥,而松松是看球赛。在松松眼里,每个球队有一个实力值ai;在妹子眼里,每个球队有bi个帅哥。
他们两个的体力都十分有限。所以,他们只能看k场比赛。
在这k场比赛中,每场有pi和qi球队对抗(不排除两队之间多次比赛和自己队与自己队比赛的情况)。松松认为每场比赛的精彩度为两队的实力值乘积,而妹子认为是帅哥的数量之和。松松心情真的炒鸡复杂,他既妒忌妹子看其他的帅哥,又十分无奈只好迁就妹子。
所以,请你写一个程序,求出妹子看到比赛的精彩度总和不小于C的情况下,松松看到比赛的精彩度的最大总和。
[输入格式]
第1行,4个正整数n,m,k,C。
第2行,N个空格隔开的正整数ai。
第3行,N个空格隔开的正整数bi。
之后M行,每行两个正整数pi,qi。
没规定不能和自己比!
[输出格式]
仅一行,一个正整数表示松松看到比赛的精彩度的最大总和。如果无论如何都无法满足妹子的要求,输出-1。
[样例]
In: |
Out: |
4 3 2 5 2 2 1 3 1 1 1 2 1 2 2 3 3 4 |
7 |
[数据范围]
对于20%数据,n,m,k<=5。对于全部数据,n<=100,k<=m<=100,ai,bi<=10
C<=1000.
三、开房间(room)
松松生日快乐
[题目描述]
松松终于钓到了妹子,可是这是天色已晚,他们只好在路边找一家旅店住下.这间旅店有很多间房间,每一间房间有一种特定的颜色,每两个房间之间有且只有一条走廊连接.显然松松和妹子是不能住在同一间房间的.但是松松又想知道他和妹子有多近.松松认为,他和妹子所在的房间之间的路径上的颜色段的数量就是他们的亲近度.现在给出有n个房间的地图以及地图上每两个相连的房间和m次操作,操作有2类:
1、将房间a到房间b最短路径上的所有房间都染成颜色c;
2、松松住在房间a,妹子住在房间b,询问松松与妹子之间的亲近度(连续相同颜色被认为是同一段),如”112221”由3段组成:”11”、”222”和”1”.
[输入格式]
第一行包含2个整数n和m,分别表示房间数和操作数; 第二行包含n个整数表示n个房间的初始颜色 下面n-1行每行包含两个整数x和y,表示x和y之间有一条走廊. “C a b c”表示这是一个染色操作,把房间a到房间b路径上所有房间(包括a和b)都染成颜色c; “Q a b”表示 这是一个询问操作,询问松松与妹子的亲近度(松松住在房间a,妹子住在房间b);
[输出格式]
对于每个询问操作,输出一行答案.
[样例输入]
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5
[样例输出]
3
1
2
[数据范围]
对于30%的数据 N,M<=100
对于100% 的数据 N<=10000,M<=100000.1<=c<=1e9
题解:
[第一题] 这是匡哥出的恶心题。原题出自[SDOI2017]新生舞会
这里我直接用原题的解析好了。
首先我们可以想到二分答案C,但C的满足条件似乎并不好处理
但我们可以利用分数规划的思想将C进行转换
C = (a1 + a2 + …… + an) / (b1 + b2 + …… + bn)
(b1 + b2 + …… + bn) * C = (a1 + a2 + …… + an)
(a1 + a2 + …… + an) - (b1 + b2 + …… + bn) * C = 0
(a1 - b1 * C) + (a2 - b2 * C) + …… + (an - bn * C) = 0
由此我们得出这道二分图的最小费用最大流:
1)将源点向每个男生连一条流量为1、费用为0的边
2)将每个女生向汇点连一条流量为1、费用为0的边
3)将每一对男女舞伴间连一条流量为1、费用为[a[i][j] - b[i][j] * C]的边
然后每次判断最小费用是否小于(或大于)0来缩小边界,最后得到答案。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const double Maxn = 1e9; const int N = 205, M = 8e4 + 5; const double eps = 1e-8; int pre[N], nxt[M], to[M], lst[N], fr[M], flw[M], Q[M]; int n, s, t, T; bool vis[N]; double a[N][N], b[N][N], cst[M], Ans, dis[N]; template <class T> inline void CkMin(T &a, const T b) {if (a > b) a = b;} inline void Add(const int x, const int y, const int z, const double g) { nxt[++T] = lst[x]; lst[x] = T; to[T] = y; fr[T] = x; flw[T] = z; cst[T] = g; nxt[++T] = lst[y]; lst[y] = T; to[T] = x; fr[T] = y; flw[T] = 0; cst[T] = -g; } inline bool Bul() { for (int i = s; i <= t; ++i) dis[i] = -Maxn; int te = 0, we = 1, x, y; dis[Q[1] = s] = 0.0; while (te < we) { x = Q[++te]; vis[x] = false; for (int i = lst[x]; i; i = nxt[i]) if (dis[y = to[i]] < dis[x] + cst[i] && flw[i]) { dis[y] = dis[x] + cst[i]; pre[y] = i; if (!vis[y]) vis[y] = true, Q[++we] = y; } } return dis[t] > -Maxn; } inline void Deal() { int Mif = Maxn; for (int i = pre[t]; i; i = pre[fr[i]]) CkMin(Mif, flw[i]); for (int i = pre[t]; i; i = pre[fr[i]]) flw[i] -= Mif, flw[i ^ 1] += Mif, Ans += (double)Mif * cst[i]; } inline bool check(const double mi) { T = 1; memset(lst, 0, sizeof(lst)); for (int i = 1; i <= n; ++i) Add(s, i, 1, 0), Add(i + n, t, 1, 0); for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) Add(i, j + n, 1, a[i][j] - mi * b[i][j]); Ans = 0.0; while (Bul()) Deal(); return (Ans <= 0); } int main() { scanf("%d", &n); s = 0; t = (n << 1) + 1; for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) scanf("%lf", &a[i][j]); for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) scanf("%lf", &b[i][j]); double l = 0, r = 1e4; while (r - l >= eps) { double mid = (l + r) / 2; (check(mid) ? r : l) = mid; } return printf("%.6lf\n", l), 0; }
[第二题] 这是我出的题目,改了一下题面,给松松一个惊喜。
这个题其实蛮好想的,众所周知,背包问题的背包容量一般来说会是固定的,但是本题并没有一个固定值作容量。
难道——真的不能用背包求解吗?事实上,这道题就是一道背包dp。
我们不妨看看,ai和bi最大是10,即妹子看一场比赛,精彩度最大是bi1+bi2=20。而m最大是100,也就是说,如果我们将妹子所看的精彩度当做容量,那么最大容量也就是2000;
所以,我们可以将此题看做一个最多取k次物品的01背包。
每两个队伍的比赛可以看做一个重量b[p[i]]+b[q[i]],价值a[p[i]]*a[q[i]]的物品。
dp[i][t][j]表示 已经选i个,本次选到了第t个,妹子满意度j时 松松的最大满意度。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N=110; int p[N],q[N],a[N],b[N],dp[N][N][20*N]; int main() { int n,m,i,j,t,k,c,ans=0; scanf("%d%d%d%d",&n,&m,&k,&c); for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=n;i++) scanf("%d",&b[i]); for (i=1;i<=m;i++) scanf("%d%d",&p[i],&q[i]); memset(dp,0,sizeof(dp)); for (i=1;i<=k;i++) for (t=i;t<=m;t++) for (j=20*m;j>=0;j--) { dp[i][t][j]=max(dp[i][t][j],dp[i][t-1][j]); if (j>=b[p[t]]+b[q[t]]) if (dp[i-1][t-1][j-b[p[t]]-b[q[t]]]>0||j==b[p[t]]+b[q[t]]) dp[i][t][j]=max(dp[i][t][j],dp[i-1][t-1][j-b[p[t]]-b[q[t]]]+a[p[t]]*a[q[t]]); if (j>=c) ans=max(ans,dp[i][t][j]); } if (ans>0) cout<<ans<<endl; else cout<<-1<<endl; return 0; }
[第三题] 这题我写过题解,就是染色那一题,题解传送门——門菛們闷扪悶
直接上代码吧:
#include <cstdio> #include <cstring> #include <iostream> #define k (z+y>>1) #define ll (r<<1) #define rr (r<<1|1) using namespace std; const int N=1e5+10;int a[N]; int ys[N],s[N*2][2],o[N],cnt,dfn,n; int zzz[N*4],yyy[N*4],laz[N*4],tot[N*4]; int d[N],siz[N],son[N],top[N],f[N],id[N]; void jia(int a,int b) { s[++cnt][1]=o[a]; s[cnt][0]=b;o[a]=cnt; return; } void shang(int r) { tot[r]=tot[ll]+tot[rr]; zzz[r]=zzz[ll];yyy[r]=yyy[rr]; if (zzz[rr]==yyy[ll]) tot[r]--; return; } void xiangxia(int r,int z,int y) { tot[ll]=tot[rr]=1; zzz[ll]=zzz[rr]=yyy[ll]=yyy[rr]=laz[ll]=laz[rr]=laz[r]; laz[r]=0;return; } void jianshu(int r,int z,int y) { if (z==y) { tot[r]=1;zzz[r]=yyy[r]=ys[a[z]]; return; } jianshu(ll,z,k);jianshu(rr,k+1,y); shang(r);return; } void gai(int r,int z,int y,int zz,int yy,int v) { if (z==zz&&y==yy) { tot[r]=1;laz[r]=zzz[r]=yyy[r]=v; return; } if (laz[r]) xiangxia(r,z,y); if (zz>k) gai(rr,k+1,y,zz,yy,v); else if (yy<=k) gai(ll,z,k,zz,yy,v); else {gai(ll,z,k,zz,k,v);gai(rr,k+1,y,k+1,yy,v);} shang(r);return; } int chaxun(int r,int z,int y,int zz,int yy) { if (z==zz&&y==yy) return tot[r]; if (laz[r]) xiangxia(r,z,y); if (zz>k) chaxun(rr,k+1,y,zz,yy); else if (yy<=k) chaxun(ll,z,k,zz,yy); else { int ans=chaxun(ll,z,k,zz,k)+chaxun(rr,k+1,y,k+1,yy); if (zzz[rr]==yyy[ll]) ans--; return ans; } } void dfs1(int x,int fa,int dep) { f[x]=fa;d[x]=dep;siz[x]=1; for (int i=o[x];i;i=s[i][1]) { if (s[i][0]!=fa) { dfs1(s[i][0],x,dep+1); siz[x]+=siz[s[i][0]]; if (siz[s[i][0]]>siz[son[x]]) son[x]=s[i][0]; } } return; } void dfs2(int x,int tp) { top[x]=tp;id[x]=++dfn;a[dfn]=x; if (son[x]) dfs2(son[x],tp); for (int i=o[x];i;i=s[i][1]) if (s[i][0]!=f[x]&&son[x]!=s[i][0]) dfs2(s[i][0],s[i][0]); return; } void ranse(int x,int y,int v) { while (top[x]!=top[y]) { if (d[top[x]]>d[top[y]]) swap(x,y); gai(1,1,n,id[top[y]],id[y],v); y=f[top[y]]; } if (d[x]>d[y]) swap(x,y); gai(1,1,n,id[x],id[y],v); return; } int newww(int r,int z,int y,int p) { if (z==y) return zzz[r]; if (laz[r]) xiangxia(r,z,y); if (p>k) return newww(rr,k+1,y,p); else return newww(ll,z,k,p); } int xunwen(int x,int y) { int ans=0,nc,fc; while (top[x]!=top[y]) { if (d[top[x]]>d[top[y]]) swap(x,y); ans+=chaxun(1,1,n,id[top[y]],id[y]); nc=newww(1,1,n,id[top[y]]); fc=newww(1,1,n,id[f[top[y]]]); y=f[top[y]];if (nc==fc) ans--; } if (d[x]>d[y]) swap(x,y); ans+=chaxun(1,1,n,id[x],id[y]); return ans?ans:1; } int main() { int m,a,b,c; cin>>n>>m;char caozuo[5]; for (int i=1;i<=n;i++) scanf("%d",&ys[i]); for (int i=1;i<n;i++) { scanf("%d%d",&a,&b);jia(a,b);jia(b,a); } dfs1(1,0,1);dfs2(1,1);jianshu(1,1,n); while (m--) { scanf("%s",caozuo); scanf("%d%d",&a,&b); switch (caozuo[0]) { case 'C':scanf("%d",&c); ranse(a,b,c);break; default: printf("%d\n",xunwen(a,b));break; } } //zhu wo zao dian AC!!! return 0; }
总结一下吧,这次最难的是第一题,想到二分+费用流不容易;第二题如果能将容量转化好,就是一道背包题;而第三题其实也就是树链剖分+线段树,只是线段树的pushup和pushdown改一下。
总之,这次考试相对其他考试,还是算简单的,毕竟我比较菜嘛!