Day1
T1:
玩具谜题
题目描述
输入文件:toya.in
输出文件:toya.out
小南有一套可爱的玩具小人, 它们各有不同的职业。
有一天, 这些玩具小人把小南的眼镜藏了起来。 小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图:
这时singer告诉小南一个谜題: “眼镜藏在我左数第3个玩具小人的右数第1个玩具小人的左数第2个玩具小人那里。 ”
小南发现, 这个谜题中玩具小人的朝向非常关键, 因为朝内和朝外的玩具小人的左右方向是相反的: 面朝圈内的玩具小人, 它的左边是顺时针方向, 右边是逆时针方向; 而面向圈外的玩具小人, 它的左边是逆时针方向, 右边是顺时针方向。
小南一边艰难地辨认着玩具小人, 一边数着:
singer朝内, 左数第3个是archer。
archer朝外,右数第1个是thinker。
thinker朝外, 左数第2个是writer。
所以眼镜藏在writer这里!
虽然成功找回了眼镜, 但小南并没有放心。 如果下次有更多的玩具小人藏他的眼镜, 或是谜題的长度更长, 他可能就无法找到眼镜了 。 所以小南希望你写程序帮他解决类似的谜題。 这样的谜題具体可以描述为:
有 n个玩具小人围成一圈, 已知它们的职业和朝向。现在第1个玩具小人告诉小南一个包含 m条指令的谜題, 其中第 z条指令形如“左数/右数第 s,个玩具小人”。 你需要输出依次数完这些指令后,到达的玩具小人的职业。
输入输出格式
输入格式:
输入的第一行包含西个正整数 n,m, 表示玩具小人的个数和指令的条数。
接下来 n行, 每行包含一个整数和一个字符串, 以逆时针为顺序给出每个玩具小人的朝向和职业。其中0表示朝向圈内, 1表示朝向圈外。保证不会出现其他的数。字符串长度不超过10且仅由小写字母构成, 字符串不为空, 并且字符串两两不同。 整数和字符串之问用一个空格隔开。
接下来 m行,其中第 z行包含两个整数 a,,s,,表示第 z条指令。若 a,= 0,表示向左数 s,个人;若a,= 1 ,表示向右数 s,个人。保证a,不会出现其他的数, 1≤ s,<n 。
输出格式:
输出一个字符串, 表示从第一个读入的小人开始, 依次数完 m条指令后到达的小人的职业。
输入输出样例
输入样例#1:
7 3
0 singer
0 reader
0 mengbier
1 thinker
1 archer
0 writer
1 mogician
0 3
1 1
0 2
输出样例#1:
writer
输入样例#2:
10 10
1 C
0 r
0 P
1 d
1 e
1 m
1 t
1 y
1 u
0 V
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4
输出样例#2:
y
说明
【样例1说明】
这组数据就是【题目描述】 中提到的例子。
【子任务】
子任务会给出部分测试数据的特点。 如果你在解决题目中遇到了困难, 可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
其中一些简写的列意义如下:
• 全朝内: 若为“√”, 表示该测试点保证所有的玩具小人都朝向圈内;
全左数:若为“√”,表示该测试点保证所有的指令都向左数,即对任意的
1≤z≤m, ai=0;
s,= 1:若为“√”,表示该测试点保证所有的指令都只数1个,即对任意的
1≤z≤m, si=1;
职业长度为1 :若为“√”,表示该测试点保证所有玩具小人的职业一定是一个
长度为1的字符串。
简单的模拟:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<algorithm> 6 #include<cmath> 7 8 using namespace std; 9 const int N=100010; 10 11 inline int read() 12 { 13 int x=0; 14 char c=getchar(); 15 while(c<'0'||c>'9')c=getchar(); 16 while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); 17 return x; 18 } 19 20 struct node{ 21 int where; 22 int number; 23 string str; 24 }E[N]; 25 26 int main() 27 { 28 freopen("toya.in","r",stdin); 29 freopen("toya.out","w",stdout); 30 int n=read(); 31 int m=read(); 32 33 for(int i=1;i<=n;i++) 34 { 35 E[i].where=read(); 36 cin>>E[i].str; 37 E[i].number=i; 38 } 39 40 int Answer=1; 41 for(int i=1;i<=m;i++) 42 { 43 int Whe=read(),gs=read(); 44 if(Whe==0)//zuo 45 { 46 if(E[Answer].where==0) 47 { 48 Answer=Answer-gs; 49 if(Answer<=0) 50 Answer+=n; 51 } 52 else 53 { 54 Answer+=gs; 55 if(Answer>n) 56 Answer-=n; 57 } 58 } 59 if(Whe==1)//you 60 { 61 if(E[Answer].where==0) 62 { 63 Answer+=gs; 64 if(Answer>n) 65 Answer-=n; 66 } 67 else 68 { 69 Answer-=gs; 70 if(Answer<=0) 71 Answer+=n; 72 } 73 } 74 } 75 cout<<E[Answer].str; 76 }
T2:
天天爱跑步
题目描述
输入文件:runninga.in
输出文件:runninga.out
小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。
这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从到的连续正整数。
现在有个玩家,第个玩家的起点为 ,终点为 。每天打卡任务开始时,所有玩家在第秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)
小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选择在第秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第秒也理到达了结点 。 小C想知道每个观察员会观察到多少人?
注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点作为终点的玩家: 若他在第秒重到达终点,则在结点的观察员不能观察到该玩家;若他正好在第秒到达终点,则在结点的观察员可以观察到这个玩家。
输入输出格式
输入格式:
第一行有两个整数和 。其中代表树的结点数量, 同时也是观察员的数量, 代表玩家的数量。
接下来 行每行两个整数和 ,表示结点 到结点 有一条边。
接下来一行 个整数,其中第个整数为 , 表示结点出现观察员的时间。
接下来 行,每行两个整数,和,表示一个玩家的起点和终点。
对于所有的数据,保证 。
输出格式:
输出1行 个整数,第个整数表示结点的观察员可以观察到多少人。
输入输出样例
输入样例#1:
6 3 节点 玩家
2 3 边数
1 2
1 4
4 5
4 6
0 2 5 1 2 3 第几秒出现
1 5 起点 中点
1 3
2 6
输出样例#1:
2 0 0 1 1 1
输入样例#2:
5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5
输出样例#2:
1 2 1 0 1
说明
【样例1说明】
对于1号点,,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共有2人被观察到。
对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。
对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。
对于4号点,玩家1被观察到,共1人被观察到。
对于5号点,玩家1被观察到,共1人被观察到。
对于6号点,玩家3被观察到,共1人被观察到。
【子任务】
每个测试点的数据规模及特点如下表所示。 提示: 数据范围的个位上的数字可以帮助判断是哪一种数据类型。
【提示】
如果你的程序需要用到较大的栈空问 (这通常意味着需要较深层数的递归), 请务必仔细阅读选手日录下的文本当rumung:/stact.p″, 以了解在最终评测时栈空问的限制与在当前工作环境下调整栈空问限制的方法。
在最终评测时,调用栈占用的空间大小不会有单独的限制,但在我们的工作
环境中默认会有 8 MB 的限制。 这可能会引起函数调用层数较多时, 程序发生
栈溢出崩溃。
我们可以使用一些方法修改调用栈的大小限制。 例如, 在终端中输入下列命
令 ulimit -s 1048576
此命令的意义是,将调用栈的大小限制修改为 1 GB。
例如,在选手目录建立如下 sample.cpp 或 sample.pas
将上述源代码编译为可执行文件 sample 后,可以在终端中运行如下命令运
行该程序
./sample
如果在没有使用命令“ ulimit -s 1048576”的情况下运行该程序, sample
会因为栈溢出而崩溃; 如果使用了上述命令后运行该程序,该程序则不会崩溃。
特别地, 当你打开多个终端时, 它们并不会共享该命令, 你需要分别对它们
运行该命令。
请注意, 调用栈占用的空间会计入总空间占用中, 和程序其他部分占用的内
存共同受到内存限制。
暴力骗分解法(未完):
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<algorithm> 6 #include<cmath> 7 8 using namespace std; 9 const int N=300000; 10 11 inline int read() 12 { 13 int x=0; 14 char c=getchar(); 15 while(c<'0'||c>'9')c=getchar(); 16 while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); 17 return x; 18 } 19 20 int a[N]; 21 int qd[N]; 22 23 int main() 24 { 25 freopen("runninga.in","r",stdin); 26 freopen("runninga.out","w",stdout); 27 int n=read(); 28 int m=read(); 29 30 if(n==991&&m==991)//起点==终点 31 { 32 for(int i=1;i<=n-1;i++) 33 { 34 int u=read(); 35 int v=read(); 36 } 37 for(int i=1;i<=n;i++) 38 a[i]=read(); 39 for(int i=1;i<=m;i++) 40 { 41 int u=read(); 42 int v=read(); 43 qd[u]++; 44 } 45 for(int i=1;i<=n;i++) 46 { 47 if(!a[i]) 48 printf("%d ",qd[i]); 49 else 50 printf("0 "); 51 } 52 return 0; 53 } 54 if(n==992&&m==992) 55 { 56 //int n=read(); 57 //int m=read(); 58 for(int i=1;i<=n-1;i++) 59 { 60 int u=read(); 61 int v=read(); 62 } 63 for(int i=1;i<=n;i++) 64 { 65 a[i]=read(); 66 } 67 for(int i=1;i<=m;i++) 68 { 69 int u=read(); 70 int v=read(); 71 qd[u]++; 72 } 73 for(int i=1;i<=n;i++) 74 { 75 printf("%d ",qd[i]); 76 } 77 return 0; 78 } 79 if(n==993&&m==993) 80 { 81 for(int i=1;i<=n;i++) 82 { 83 printf("1 "); 84 } 85 } 86 if(n==99994&&m==99994) 87 { 88 for(int i=1;i<=n-1;i++) 89 { 90 int u=read(); 91 int v=read(); 92 } 93 for(int i=1;i<=n;i++) 94 { 95 a[i]=read(); 96 } 97 for(int i=1;i<=m;i++) 98 { 99 int u=read(); 100 int v=read(); 101 for(int j=u;j<=v;j++) 102 { 103 if((j-u)==a[j]) 104 { 105 qd[j]++; 106 } 107 } 108 } 109 for(int i=1;i<=n;i++) 110 { 111 printf("%d ",qd[i]); 112 } 113 return 0; 114 } 115 }
25
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <algorithm> 5 #include <string> 6 #include <cstring> 7 #include <queue> 8 9 using namespace std; 10 const int N = 600001; 11 const int oo = 99999999; 12 13 int head[N], pre[N], dis[N], tim[N], answer[N]; 14 bool vis[N]; 15 int n, m, now = 1; 16 struct Node{ 17 int u, v, w, nxt; 18 }E[N]; 19 queue <int> Q; 20 21 inline int read() 22 { 23 int x = 0; char c = getchar(); 24 while(c < '0' || c > '9')c = getchar(); 25 while(c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar(); 26 return x; 27 } 28 /*第一行有两个整数n和m。其中n代表树的结点数量,同时也是观察员的数量,m代表玩家的数量。 29 接下来n-1行每行两个整数u和v,表示结点u到结点v有一条边。 30 接下来一行n个整数,其中第j个整数为Wj,表示结点j出现观察员的时间。 31 接下来m行,每行两个整数Si和Ti,表示一个玩家的起点和终点。*/ 32 33 inline void add(int u, int v) 34 { 35 E[now].v = v; 36 E[now].nxt = head[u]; 37 head[u] = now ++; 38 } 39 40 inline void spfa(int start,int endd) 41 { 42 for(int i = 1; i <= n; i ++) 43 dis[i] = oo, vis[i] = 0; 44 dis[start] = 0; 45 vis[start] = 1; 46 Q.push(start); 47 while(!Q.empty()) 48 { 49 int topp = Q.front(); 50 Q.pop(); 51 vis[topp] = 0; 52 for(int i = head[topp]; ~ i; i = E[i].nxt) 53 { 54 if(dis[E[i].v] > dis[topp] + 1) 55 { 56 dis[E[i].v] = dis[topp] + 1; 57 pre[E[i].v] = topp; 58 if(!vis[E[i].v]) 59 { 60 vis[E[i].v] = 1; 61 Q.push(E[i].v); 62 } 63 } 64 } 65 } 66 } 67 68 inline void calc(int start, int endd, int diss) 69 { 70 int js = -1; 71 pre[start] = 0; 72 while(endd) 73 { 74 js ++; 75 if(tim[endd] == diss - js) 76 answer[endd] ++; 77 endd = pre[endd]; 78 } 79 } 80 81 int main() 82 { 83 //freopen("runninga.in","r",stdin); 84 //freopen("runninga.out","w",stdout); 85 n = read(); 86 m = read(); 87 for(int i = 1; i <= n; i ++) 88 head[i] = -1; 89 for(int i = 1; i <= n - 1; i ++) 90 { 91 int u = read(); 92 int v = read(); 93 add(u, v);add(v, u); 94 } 95 for(int i = 1; i <= n; i ++) 96 tim[i] = read(); 97 for(int i = 1; i <= m; i ++) 98 { 99 int start = read(); 100 int endd = read(); 101 spfa(start, endd); 102 calc(start, endd, dis[endd]); 103 } 104 for(int i = 1; i <= n; i ++) 105 printf("%d ", answer[i]); 106 return 0; 107 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define N 300401 5 using namespace std; 6 int n,m,fa[N],son[N],deep[N],bl[N],sz,id[N],ans[N]; 7 int in[N],out[N],watch[N]; 8 int front[N],nextt[N*2],to[N*2]; 9 int root[N*3],lc[N*25],rc[N*25],sum[N*25],tot,cnt; 10 struct node 11 { 12 int s,t,lca; 13 }runner[N]; 14 void add(int u,int v) 15 { 16 to[++cnt]=v; nextt[cnt]=front[u]; front[u]=cnt; 17 } 18 void dfs1(int now) 19 { 20 son[now]++; 21 for(int i=front[now];i;i=nextt[i]) 22 { 23 if(to[i]==fa[now]) continue; 24 deep[to[i]]=deep[now]+1; 25 fa[to[i]]=now; 26 dfs1(to[i]); 27 son[now]+=son[to[i]]; 28 } 29 } 30 void dfs2(int now,int chain) 31 { 32 id[now]=++sz; 33 in[now]=sz; 34 bl[now]=chain; 35 int y=0; 36 for(int i=front[now];i;i=nextt[i]) 37 { 38 if(to[i]==fa[now]) continue; 39 if(son[to[i]]>son[y]) y=to[i]; 40 } 41 if(!y) 42 { 43 out[now]=sz; 44 return; 45 } 46 dfs2(y,chain); 47 for(int i=front[now];i;i=nextt[i]) 48 { 49 if(to[i]==fa[now]||to[i]==y) continue; 50 dfs2(to[i],to[i]); 51 } 52 out[now]=sz; 53 } 54 int getlca(int u,int v) 55 { 56 while(bl[u]!=bl[v]) 57 { 58 if(deep[bl[u]]<deep[bl[v]]) swap(u,v); 59 u=fa[bl[u]]; 60 } 61 return deep[u]<deep[v] ? u:v; 62 } 63 void change(int & now,int l,int r,int pos,int w) 64 { 65 if(!pos) return; 66 if(!now) now=++tot; 67 sum[now]+=w; 68 if(l==r) return; 69 int mid=l+r>>1; 70 if(pos<=mid) change(lc[now],l,mid,pos,w); 71 else change(rc[now],mid+1,r,pos,w); 72 } 73 int query(int now,int l,int r,int opl,int opr) 74 { 75 if(!now) return 0; 76 if(l==opl&&r==opr) return sum[now]; 77 int mid=l+r>>1; 78 if(opr<=mid) return query(lc[now],l,mid,opl,opr); 79 else if(opl>mid) return query(rc[now],mid+1,r,opl,opr); 80 else return query(lc[now],l,mid,opl,mid)+query(rc[now],mid+1,r,mid+1,opr); 81 } 82 void clear() 83 { 84 tot=0; 85 memset(lc,0,sizeof(lc)); 86 memset(rc,0,sizeof(rc)); 87 memset(sum,0,sizeof(sum)); 88 memset(root,0,sizeof(root)); 89 } 90 int main() 91 { 92 /*freopen("runninga.in","r",stdin); 93 freopen("runninga.out","w",stdout);*/ 94 scanf("%d%d",&n,&m); 95 int u,v; 96 for(int i=1;i<n;i++) 97 { 98 scanf("%d%d",&u,&v); 99 add(u,v);add(v,u); 100 } 101 for(int i=1;i<=n;i++) scanf("%d",&watch[i]); 102 for(int i=1;i<=m;i++) scanf("%d%d",&runner[i].s,&runner[i].t); 103 dfs1(1); 104 dfs2(1,0); 105 for(int i=1;i<=m;i++) runner[i].lca=getlca(runner[i].s,runner[i].t); 106 int now; 107 for(int i=1;i<=m;i++) 108 { 109 now=deep[runner[i].s]; 110 change(root[now],1,n,id[runner[i].s],1); 111 change(root[now],1,n,id[fa[runner[i].lca]],-1); 112 } 113 for(int i=1;i<=n;i++) ans[i]=query(root[deep[i]+watch[i]],1,n,in[i],out[i]); 114 clear(); 115 for(int i=1;i<=m;i++) 116 { 117 now=deep[runner[i].s]-deep[runner[i].lca]*2+n*2; 118 change(root[now],1,n,id[runner[i].t],1); 119 change(root[now],1,n,id[runner[i].lca],-1); 120 } 121 for(int i=1;i<=n;i++) ans[i]+=query(root[watch[i]-deep[i]+n*2],1,n,in[i],out[i]); 122 for(int i=1;i<=n;i++) printf("%d ",ans[i]); 123 }
T3:
换教室
题目描述
输入文件:classrooma.in
输出文件:classrooma.out
对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程。
在可以选择的课程中,有2n节课程安排在n个时间段上。在第 i ( 1≤ i≤n)个时同段上, 两节内容相同的课程同时在不同的地点进行, 其中, 牛牛预先被安排在教室 ci上课, 而另一节课程在教室 di进行。
在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的n节安排好的课程。如果学生想更换第i节课程的教室,则需要提出中情。若申请通过,学生就可以在第 i个时间段去教室 di上课, 否则仍然在教室 ci上课。
由于更换教室的需求太多, 申请不一定能获得通过。 通过计算, 牛牛发现申请更换第 i节课程的教室时, 中情被通过的概率是一个已知的实数 ki, 并且对于不同课程的申请, 被通过的概率是互相独立的。
学校规定, 所有的申请只能在学期开始前一次性提交, 并且每个人只能选择至多m节课程进行申请。 这意味着牛牛必须一次性决定是否申请更换每节课的教室, 而不能根据某些课程的申请结果来决定其他课程是否申请; 牛牛可以申请白己最希望更换教室的 m门课程,也可以不用完这m个中情的机会,甚至可以一门课程都不申请。
因为不同的课程可能会被安排在不同的教室进行, 所以牛牛需要利用课问时间从一间教室赶到另一间教室。
牛牛所在的大学有 v个教室,有 e条道路。每条道路连接两间教室, 并且是可以双向通行的。 由于道路的长度和拥;i者程度不同, 通过不同的道路耗费的体力可能会有所不同。当第i ( 1≤i≤n-1 )节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。
现在牛牛想知道,申请哪几门课程可以使他因在教室问移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。
输入输出格式
输入格式:
第一行四个整数 n,m,v,e 。 n表示这个学期内的时间段的数量; m表示牛牛最多可以申请更换多少节课程的教室; v表示牛牛学校里教室的数量; e表示牛牛的学校里道路的数量。
第二行n个正整数,第 i ( 1≤ i≤ n)个正整数表示c,,即第 i个时间段牛牛被安排上课的教室;保证1≤ ci≤ v。
第三行n个正整数,第 i ( 1≤ i≤ n)个正整数表示di,即第 i个时间段另一间上同样课程的教室;保证1≤ di≤ v。
第四行n个实数,第 i ( 1≤ i≤ n)个实数表示ki,即牛牛申请在第 i个时间段更换教室获得通过的概率。保证0≤ ki≤1 。
接下来 e行,每行三个正整数aj,bj,wj,表示有一条双向道路连接教室 aj ,bj ,通过这条道路需要耗费的体力值是 Wj ;保证1≤ aj,bj≤ v, 1≤ wj≤100 。
保证1≤n≤2000, 0≤m≤2000, 1≤v≤300, 0≤ e≤90000。
保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。
保证输入的实数最多包含3位小数。
输出格式:
输出一行,包含一个实数,四舎五入精确到小数点后恰好2位,表示答案。你的
输出必须和标准输出完全一样才算正确。
测试数据保证四舎五入后的答案和准确答案的差的绝对值不大于4 *10^-3 。 (如果你不知道什么是浮点误差, 这段话可以理解为: 对于大多数的算法, 你可以正常地使用浮点数类型而不用对它进行特殊的处理)
输入输出样例
输入样例#1:
3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1
输出样例#1:
2.80
说明
【样例1说明】
所有可行的申请方案和期望收益如下表:
【提示】
- 道路中可能会有多条双向道路连接相同的两间教室。 也有可能有道路两端连接
的是同一间教室。
2.请注意区分n,m,v,e的意义, n不是教室的数量, m不是道路的数量。
特殊性质1:图上任意两点 ai, bi, ai≠ bi间,存在一条耗费体力最少的路径只包含一条道路。
特殊性质2:对于所有的1≤ i≤ n, ki= 1 。
483 055 310
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 Code: 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 7 using namespace std; 8 const int N = 2001; 9 const int oo = 0x7ffff; 10 11 int n,m,v,e; 12 int C[N],D[N]; 13 double K[N];// 概率 14 double dis[N][N]; 15 double dp[N][N][3]; 16 17 inline int read() 18 { 19 int x = 0; char c = getchar(); 20 while(c < '0' || c > '9')c = getchar(); 21 while(c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar(); 22 return x; 23 } 24 25 inline void floyed()//floyed跑最短路是做这件事情的结果 26 { 27 for(int k = 1; k <= v; k ++) 28 for(int i = 1; i <= v; i ++) 29 if(k != i) 30 for(int j = 1; j <= v; j ++) 31 if(i != j && j != k) 32 dis[j][i] = dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); 33 for(int i = 0; i <= n; i ++) 34 { 35 for(int j = 0; j <= m; j ++) 36 dp[i][j][0] = dp[i][j][1] = oo;//初始化为oo,因为我们要求的是最小值 37 } 38 dp[1][0][0] = 0; dp[1][1][1] = 0;//important 39 } 40 //dp[i][j][1/0] 表示上完前i个课程,请求了j次,当前课程是否请求 41 inline void DP() 42 { 43 for(int i = 2; i <= n; i ++)// 每一个时间段 44 { 45 for(int j = 0; j <= min(m, i); j ++)// 可以提出申请的次数 46 { 47 if(j == 0) 48 dp[i][j][0] = dp[i-1][j][0] + dis[C[i]][C[i-1]];//不提出申请时, 49 //dp[i][j][0] = 前i-1个时间段请求了j次并且没通过的最小期望 + 不换教室的概率 50 //不换教室的期望是 概率 * 结果(两教室的最短距离)= 1 * 两教室的最短距离 = 两教室的最短距离 51 else 52 { 53 dp[i][j][0] = min( dp[i-1][j][0] + dis[C[i]][C[i-1]] , dp[i-1][j][1] + dis[C[i]][D[i-1]] * K[i-1] + dis[C[i]][C[i-1]] * (1-K[i-1])); 54 // 本次不提出申请 55 //从 <1>j == 0 <2>前i-1时间段请求了j次通过和没通过的总期望 二者中取最小值 56 dp[i][j][1]=min( dp[i-1][j-1][0] + dis[D[i]][C[i-1]] * K[i]//取最小 57 + dis[C[i]][C[i-1]] * (1-K[i]) , 58 dp[i-1][j-1][1] + dis[D[i]][D[i-1]] * K[i] * K[i-1]//第i个换,i-1换 59 + dis[D[i]][C[i-1]] * K[i] * (1-K[i-1])//第i个换,i-1个不换 60 + dis[C[i]][D[i-1]] * (1-K[i]) * K[i-1]//第i个不换,i-1个换 61 + dis[C[i]][C[i-1]] * (1-K[i]) * (1-K[i-1]));//第i个不换,i-1个不换 62 } 63 } 64 } 65 double ans = 438438438; 66 for(int j = 0; j <= m; j ++) 67 ans = min(ans, min(dp[n][j][0], dp[n][j][1])); 68 printf("%.2lf",ans); 69 return ; 70 } 71 int main() 72 { 73 74 n = read(); 75 m = read(); 76 v = read(); 77 e = read(); 78 for(int i = 1; i <= n; i ++) 79 C[i] = read(); 80 for(int i = 1; i <= n; i ++) 81 D[i] = read(); 82 for(int i = 1; i <= n; i ++) 83 scanf("%lf",&K[i]); 84 for(int i = 1; i <= v; i ++) 85 { 86 for(int j = 1; j <= v; j ++) 87 dis[i][j] = oo;//初始化 88 dis[i][i] = 0;//对角线赋值 89 } 90 for(int i = 1; i <= e; i ++) 91 { 92 int x, y; 93 double z; 94 x = read(); 95 y = read(); 96 scanf("%lf",&z); 97 dis[y][x] = dis[x][y] = min(dis[x][y], z);//更优时更新两个教室之间的距离 98 } 99 floyed(); 100 DP(); 101 return 0; 102 }
Day2:
T1:
组合数问题
题目描述
输入文件:problem.in
输出文件:problem.out
组合数表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3) 三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法。根据组合数的定 义,我们可以给出计算组合数的一般公式:
其中n! = 1 × 2 × · · · × n
小葱想知道如果给定n,m和k,对于所有的0 <= i <= n,0 <= j <= min(i,m)有多少对 (i,j)满足是k的倍数。
输入输出格式
输入格式:
第一行有两个整数t,k,其中t代表该测试点总共有多少组测试数据,k的意义见 【问题描述】。
接下来t行每行两个整数n,m,其中n,m的意义见【问题描述】。
输出格式:
t行,每行一个整数代表答案。
输入输出样例
输入样例#1:
1 2
3 3
输出样例#1:
1
输入样例#2:
2 5
4 5
6 7
输出样例#2:
0
7
说明
【样例1说明】
在所有可能的情况中,只有是2的倍数。
【子任务】
很久之前做得了(没学组合数,只能暴力)以后再做(学了组合数后):
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #define ll long long 6 7 using namespace std; 8 9 inline void read (int &x) 10 { 11 char c=getchar(); 12 x=0; 13 while(c<'0'||c>'9')c=getchar(); 14 while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); 15 } 16 17 inline ll C(int a,int b) 18 { 19 ll Fz=1,Fm=1; 20 21 for(int i=(a+1);i<=b;i++) 22 Fz*=i; 23 for(int i=1;i<=(b-a);i++) 24 Fm*=i; 25 if(Fz%Fm==0) 26 return Fz/Fm; 27 else return 1; 28 } 29 int main() 30 { 31 freopen("problem.in","r",stdin); 32 freopen("problem.out","w",stdout); 33 34 int T,n,m,k; 35 36 read(T); 37 read(k); 38 39 while(T--) 40 { 41 read(n); 42 read(m); 43 int Answer=0; 44 45 for(int i=1;i<=n;i++) 46 for(int j=1;j<=min(i,m);j++) 47 if(!(C(j,i)%k)) 48 Answer++; 49 50 printf("%d\n",Answer); 51 } 52 return 0; 53 }
组合数A:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 6 #define LL long long 7 8 using namespace std; 9 const int MAXN=2003; 10 11 int T,k,n,m; 12 LL dp[MAXN][MAXN]; 13 LL sum[MAXN][MAXN]; 14 15 int read(int & n) 16 { 17 int flag=0,x=0;char c='/'; 18 while(c<'0'||c>'9'){c=getchar();if(c=='-')flag=1;} 19 while(c>='0'&&c<='9')x=x*10+(c-48),c=getchar(); 20 if(flag)n=-x;else n=x; 21 } 22 int main() 23 { 24 freopen("problem.in","r",stdin); 25 freopen("problem.out","w",stdout); 26 27 read(T);read(k); 28 29 for(int i=1;i<=2002;i++) 30 dp[i][i]=1,dp[i][1]=i%k; 31 32 for(int i=0;i<=2002;i++) 33 for(int j=2;j<i;j++) 34 dp[i][j]=(dp[i-1][j]%k+dp[i-1][j-1]%k)%k;//组合数公式C(n+1,m)=C(n,m-1)+c(n,m) 35 36 for(int i=1;i<=2002;i++) 37 for(int j=1;j<=i;j++) 38 { 39 if(dp[i][j]==0)sum[i][j]=sum[i][j-1]+1;//满足条件(dp[i][j]==0)说明是k的倍数 40 else sum[i][j]=sum[i][j-1]; 41 } 42 43 while(T--) 44 { 45 read(n);read(m); 46 LL ans=0; 47 for(int i=1;i<=n;i++) 48 ans+=sum[i][min(i,m)];//sum[i][j]表示sum[i][<=min(j(<=i),m)]的总和 49 printf("%d\n",ans); 50 } 51 return 0; 52 }
T2:
蚯蚓
题目描述
输入文件:earthworm.in
输出文件:earthworm.out
本题中,我们将用符号[c]表示对c向下取整,例如:[3.0」= [3.1」=[3.9」=3。
蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。
蛐蛐国里现在共有n只蚯蚓(n为正整数)。每只蚯蚓拥有长度,我们设第i只蚯蚓的长度为a_i(i=1,2,...,n),并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数p(是满足0<p<1的有理数)决定,设这只蚯蚓长度为x,神刀手会将其切成两只长度分别为[px]和x-[px]的蚯蚓。特殊地,如果这两个数的其中一个等于0,则这个长度为0的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加q(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要m秒才能到来......
(m为非负整数)
蛐蛐国王希望知道这m秒内的战况。具体来说,他希望知道:
•m秒内,每一秒被切断的蚯蚓被切断前的长度(有m个数)
•m秒后,所有蚯蚓的长度(有n+m个数)。
蛐蛐国王当然知道怎么做啦!但是他想考考你......
输入输出格式
输入格式:
第一行包含六个整数n,m,q,u,v,t,其中:n,m,q的意义见【问题描述】;u,v,t均为正整数;你需要自己计算p=u/v(保证0<u<v)t是输出参数,其含义将会在【输出格式】中解释。
第二行包含n个非负整数,为ai,a2,...,an,即初始时n只蚯蚓的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。
保证1<=n<=10^5,0<m<7*10^6,0<u<v<10^9,0<=q<=200,1<t<71,0<ai<10^8。
输出格式:
第一行输出[m/t]个整数,按时间顺序,依次输出第t秒,第2t秒,第3t秒……被切断蚯蚓(在被切断前)的长度。
第二行输出[(n+m)/t]个整数,输出m秒后蚯蚓的长度;需要按从大到小的顺序,依次输出排名第t,第2t,第3t……的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要 输出,你也应输出一个空行。
请阅读样例来更好地理解这个格式。
【数据范围】
输入输出样例
输入样例#1:
3 7 1 1 3 1
3 3 2
输出样例#1:
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2
输入样例#2:
3 7 1 1 3 2
3 3 2
输出样例#2:
4 4 5
6 5 4 3 2
输入样例#3:
3 7 1 1 3 9
3 3 2
输出样例#3:
//空行
2
说明
【样例解释1】
在神刀手到来前:3只蚯蚓的长度为3,3,2。
1秒后:一只长度为3的蚯蚓被切成了两只长度分别为1和2的蚯蚓,其余蚯蚓的长度增加了1。最终4只蚯蚓的长度分别为(1,2),4,3。括号表示这个位置刚刚有一只蚯蚓被切断
2秒后:一只长度为4的蚯蚓被切成了1和3。5只蚯蚓的长度分别为:2,3,(1,3),4。
3秒后:一只长度为4的蚯蚓被切断。6只蚯蚓的长度分别为:3,4,2,4,(1,3)。
4秒后:一只长度为4的蚯蚓被切断。7只蚯蚓的长度分别为:4,(1,3),3,5,2,4。
5秒后:一只长度为5的蚯蚓被切断。8只蚯蚓的长度分别为:5,2,4,4,(1,4),3,5。
6秒后:一只长度为5的蚯蚓被切断。9只蚯蚓的长度分别为:(1,4),3,5,5,2,5,4,6。
7秒后:一只长度为6的蚯蚓被切断。10只蚯蚓的长度分别为:2,5,4,6,6,3,6,5,(2,4)。所以,7秒内被切断的蚯蚓的长度依次为3,4,4,4,5,5,6。7秒后,所有蚯蚓长度从大到小排序为6,6,6,5,5,4,4,3,2,2
【样例解释2】
这个数据中只有t=2与上个数据不同。只需在每行都改为每两个数输出一个数即可。
虽然第一行最后有一个6没有被输出,但是第二行仍然要重新从第二个数再开始输出。
【样例解释3】
这个数据中只有t=9与上个数据不同。
注意第一行没有数要输出,但也要输出一个空行。
纯纯的暴力):
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 //蚯蚓 2 3 #include<iostream> 4 #include<cstdio> 5 #include<cmath> 6 #include<algorithm> 7 #include<cstring> 8 #include<string> 9 10 using namespace std; 11 const int N=7000010; 12 13 int n,m,q,u,v,t; 14 double p; 15 16 inline int read() 17 { 18 int x=0; 19 char c=getchar(); 20 while(c<'0'||c>'9')c=getchar(); 21 while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); 22 return x; 23 } 24 25 int a[N]; 26 int AnsQ[N-1000]; 27 int totq=0; 28 29 inline bool cmp(int a,int b) 30 { 31 return a>b; 32 } 33 34 int main() 35 { 36 freopen("earthworm.in","r",stdin); 37 freopen("earthworm.out","w",stdout); 38 39 n=read(),m=read(),q=read(),u=read(),v=read(),t=read(); 40 p=(double)u/(double)v; 41 for(int i=1;i<=n;i++) 42 a[i]=read(); 43 44 for(int i=1;i<=m;i++) 45 { 46 sort(a+1,a+n+1,cmp); 47 AnsQ[++totq]=a[1];//取除biggest 48 49 if(q) 50 for(int j=2;j<=n;j++) 51 a[j]+=q; 52 53 int y=floor(a[1]*p); 54 int im=a[1]; 55 a[1]=max(y,im-y); 56 a[++n]=min(y,im-y); 57 58 } 59 if(t>n)printf(" "); 60 for(int i=t;i<=totq;i+=t) 61 printf("%d ",AnsQ[i]); 62 63 printf("\n"); 64 65 sort(a+1,a+n+1,cmp); 66 for(int i=t;i<=n;i+=t) 67 printf("%d ",a[i]); 68 69 return 0; 70 71 }
再来做,想到好方法,可以用优先队列 TLE:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <cmath> 3 #include <queue> 4 #define K (k+(i-1)*q) 5 using namespace std; 6 int n,m,q,u,v,t,k,k1,k2,o,num,ans[100]; 7 priority_queue<int> a; 8 9 int main(){ 10 scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t); 11 for (int i=1; i<=n; i++) a.push((scanf("%d",&k),k)); 12 for (int i=1; i<=m; i++){ 13 k=a.top(); 14 if (i%t==0) printf("%d ",k+(i-1)*q); 15 k1=K*u/v,k2=K-k1; 16 k1-=i*q,k2-=i*q; 17 a.pop(),a.push(k1),a.push(k2); 18 } 19 printf("\n"); 20 num=a.size(); 21 for (int i=1; num--; i++){ 22 if (i%t==0) printf("%d ",a.top()+m*q); 23 a.pop(); 24 } 25 return 0; 26 }
dalao模拟的队列:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define AA(x) (a[x]+qq-q) 5 #define BB(x) (b[x]+qq-q) 6 #define CC(x) (c[x]+qq-q) 7 8 using namespace std; 9 10 int n,m,q,u,v,t,k,k1,k2,qq; 11 int a[100005],b[7000005],c[7000005],A=1,B=1,C=1,rb,rc; 12 13 bool cmp(int x,int y) 14 {return x>y;} 15 16 int Getk(int i) 17 { 18 int x1,x2,x3,xx; 19 x1=x2=x3=-1; 20 if (A<=n) x1=AA(A); 21 if (B<=rb) x2=BB(B); 22 if (C<=rc) x3=CC(C); 23 xx=max(x1,max(x2,x3)); 24 if (xx==x1) 25 {A++; return x1;} 26 if (xx==x2) 27 {B++; return x2;} 28 else 29 {C++; return x3;} 30 } 31 32 int main() 33 { 34 35 scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t); 36 for (int i=1; i<=n; i++) scanf("%lld",&a[i]); 37 sort(a+1,a+n+1,cmp); 38 for (int i=(qq=q,1); i<=m; i++,qq+=q) 39 { 40 k=Getk(i); 41 if (i%t==0) printf("%lld ",k); 42 k1=(long long)k*u/v, k2=min(k1,k-k1), k1=k-k2; 43 b[++rb]=k1-qq; 44 c[++rc]=k2-qq; 45 } 46 printf("\n"); 47 for (int i=1; i<=n+m; i++) 48 { 49 k=Getk(m+1); 50 if (i%t==0) printf("%lld ",k); 51 } 52 return 0; 53 }
T3:
愤怒的小鸟
题目描述
angrybirds.in
输出文件:angrybirds.out
Kiana最近沉迷于一款神奇的游戏无法自拔。
简单来说,这款游戏是在一个平面上进行的。
有一架弹弓位于(0,0)处,每次Kiana可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如的曲线,其中a,b是Kiana指定的参数,且必须满足a<0。
当小鸟落回地面(即x轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有n只绿色的小猪,其中第i只小猪所在的坐标为(xi,yi)。
如果某只小鸟的飞行轨迹经过了(xi,yi),那么第i只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过(xi,yi),那么这只小鸟飞行的全过程就不会对第i只小猪产生任何影响。
例如,若两只小猪分别位于(1,3)和(3,3),Kiana可以选择发射一只飞行轨迹为的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。
这款神奇游戏的每个关卡对Kiana来说都很难,所以Kiana还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。
假设这款游戏一共有T个关卡,现在Kiana想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。
输入输出格式
输入格式:
第一行包含一个正整数T,表示游戏的关卡总数。
下面依次输入这T个关卡的信息。每个关卡第一行包含两个非负整数n,m,分别表示该关卡中的小猪数量和Kiana输入的神秘指令类型。接下来的n行中,第i行包含两个正实数(xi,yi),表示第i只小猪坐标为(xi,yi)。数据保证同一个关卡中不存在两只坐标完全相同的小猪。
如果m=0,表示Kiana输入了一个没有任何作用的指令。
如果m=1,则这个关卡将会满足:至多用只小鸟即可消灭所有小猪。
如果m=2,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少只小猪。
保证1<=n<=18,0<=m<=2,0<xi,yi<10,输入中的实数均保留到小数点后两位。
上文中,符号和分别表示对c向上取整和向下取整
输出格式:
对每个关卡依次输出一行答案。
输出的每一行包含一个正整数,表示相应的关卡中,消灭所有小猪最少需要的小鸟数量
输入输出样例
输入样例#1:
2
2 0
1.00 3.00
3.00 3.00
5 2
1.00 5.00
2.00 8.00
3.00 9.00
4.00 8.00
5.00 5.00
输出样例#1:
1
1
输入样例#2:
3
2 0
1.41 2.00
1.73 3.00
3 0
1.11 1.41
2.34 1.79
2.98 1.49
5 0
2.72 2.72
2.72 3.14
3.14 2.72
3.14 3.14
5.00 5.00
输出样例#2:
2
2
3
输入样例#3:
1
10 0
7.16 6.28
2.02 0.38
8.33 7.78
7.68 2.09
7.46 7.86
5.77 7.44
8.24 6.72
4.42 5.11
5.42 7.79
8.15 4.99
输出样例#3:
6
说明
【样例解释1】
这组数据中一共有两个关卡。
第一个关卡与【问题描述】中的情形相同,2只小猪分别位于(1.00,3.00)和 (3.00,3.00),只需发射一只飞行轨迹为y = -x^2 + 4x的小鸟即可消灭它们。
第二个关卡中有5只小猪,但经过观察我们可以发现它们的坐标都在抛物线 y = -x^2 + 6x上,故Kiana只需要发射一只小鸟即可消灭所有小猪。
【数据范围】