解题过程
开场shl过B,C,然后lfw写J,J WA了以后shl写A,但是因为OJ上空间开小WA了,而不是MLE?,J加了特判过了。之后一直在检查A错哪了,直到qt发现问题改了空间,浪费许多时间,但是拿到A一血,shl和byf推出K,不会写组合数而抄了板子, 但是WA, shl想出E题拿到一血了,byf和 shl以为板子错了,让lfw重写K题
lfw重写K题的时候公式复制了之前的代码,又WA了,lfw看出I题,开始写,然后因为高精度加法时c[i]=a[i]+b[i]而不是c[i]+=a[i]+b[i]导致了没有进位,WA了,shl这段时间一直在调D,WA了2发后拿D一血,lfw最后终于发现K题公式中连续3个int级别的数字相乘,中间没有取模,会爆long long ,改了A了,最后lfw发现I题问题,但是因为spj挂了以为没有A,不过最后还是过了。
A Altruistic Amphibians
题目大意:有n只青蛙想从一个深度为d的井中跳出去,因此青蛙需要叠罗汉。青蛙有3个属性,体重,身高,和跳跃力。叠罗汉唯一一个限制就是每只青蛙身上的青蛙的体重之和不能超过自身的体重。青蛙的跳跃力加上身下的青蛙的身高之和如果严格大于d,青蛙就能跳出去。 n<1e5,体重之和,d<1e8;
题解:考虑使用dp dp[i]表示的是能承重w的最大高度(注意这里是承重,不是下面青蛙的总重量)。 dp前需要将青蛙按体重从大到小排序,因为重的青蛙肯定不能在轻的青蛙上面。 因此转移方程就是dp[j]=max(dp[j],h[i]+dp[w[i]+j])(for j in range[1,wi-1]);时间复杂度 O(n)
参考代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define clr(a,v) memset(a,v,sizeof(a)) 4 typedef long long ll; 5 const int maxn=1e5+10; 6 const int maxm=1e8+10; 7 struct Node{ 8 int l,w,h; 9 operator<(Node b)const {return w>b.w;} 10 } node[maxn]; 11 int n,d,dp[maxm],ans; 12 13 int main() 14 { 15 scanf("%d%d",&n,&d); 16 for(int i=1;i<=n;++i) 17 scanf("%d%d%d",&node[i].l,&node[i].w,&node[i].h); 18 sort(node+1,node+1+n); 19 clr(dp,0); ans=0; 20 for(int i=1;i<=n;++i) 21 { 22 if(dp[node[i].w]+node[i].l>d) ans++; 23 for(int j=node[i].w+1;j<min(node[i].w*2,maxm);++j) 24 dp[j-node[i].w]=max(dp[j-node[i].w],dp[j]+node[i].h); 25 } 26 printf("%d ",ans); 27 return 0; 28 }
B Baby Bites
题目大意:给定n,和一个带有mumble的序列,mumble可以代表任意数字,问该序列可否能为从1开始的连续的序列。 题解:for循环对数字进行判断即可。 复杂度:O(n)
参考代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define clr(a,v) memset(a,v,sizeof(a)) 4 typedef long long ll; 5 const int INF=0x3f3f3f3f; 6 const int maxn=1e5+10; 7 int n,num; 8 char s[maxn]; 9 int work(char s[]) 10 { 11 int len=strlen(s),x=0; 12 for(int i=0;i<len;++i) x=x*10+s[i]-'0'; 13 return x; 14 } 15 int main() 16 { 17 scanf("%d",&n); 18 bool flag=false; 19 for(int i=1;i<=n;++i) 20 { 21 scanf("%s",s); 22 if(s[0]>='0' && s[0]<='9') 23 { 24 num=work(s); 25 if(num!=i) flag=true; 26 } 27 } 28 if(flag) puts("something is fishy"); 29 else puts("makes sense"); 30 31 return 0; 32 }
C Code Cleanups
题解:每次输入就看现在有了多少垃圾,垃圾数量达到20就进行清理 两次相邻输入之间增加的垃圾数为: 垃圾代码数量*相邻天数之差 输入结束看还有没有垃圾代码没被清理
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define clr(a,v) memset(a,v,sizeof(a)) 4 typedef long long ll; 5 int n,d[400],sum,x,ans,tmp; 6 int main() 7 { 8 scanf("%d",&n); 9 clr(d,0); ans=sum=tmp=0; 10 for(int i=1;i<=n;++i) scanf("%d",&x),d[x]++; 11 for(int i=1;i<=365;++i) 12 { 13 if(d[i]) tmp++; 14 sum+=tmp; 15 if(sum>=20) ans++,sum=0,tmp=0; 16 } 17 if(tmp) ans++; 18 printf("%d ",ans); 19 return 0; 20 }
D:Delivery Delays
题意:
1000个点,5000条边的无向图,披萨店在1号店.1000份披萨订单,每个订单有下单时间,送达地点,披萨制作出来的时间.你是快递员初始在1号点,每次可以拿无穷多披萨,送完以后返回1号点继续送,送餐的时候要求按照下单顺序送达,求等待时间最长的顾客的最小等待时间.
题解:最小化最大值的问题,我们一般用二分答案再judge的套路进行. 考虑到所有的披萨都必须按照下单时间顺序送达,那么可以想象到最优的方案应该会将披萨序列分成若干小段,每一段都是从1号点出发,拿上该段所有的披萨,然后以最短路的形式,依次将披萨送达,最后回到1点. 对于最短路的处理,我们根据题目数据范围:点数n<=1000,边数m<=5000;我们可以进行n次Dijkstra求出任意两点之间的最短时间( O(n^2log(m)) ). 将序列分成若干段,我们可以考虑DP:dp[i]表示前i个订单已经送达且回到1号点的最短时间; 对于i这个点,将所有j≥i+1 && j<=k的dp[j]全部更新. 定义len(i,j) :表示从1出发,依次经过i,i+1,…,j这些点的最短路径长度.
当用dp[i]来更新dp[j]的时候我们注意到出发时间一定不能小于订单的完成时间 max{t[i+1],t[i+2],...,t[j]} ,因为必须等这些披萨都制作完成后才能出发 并且出发时间也一定不能大于最晚时间(即用ans求出的最晚时间);min{ans+s[i+1]−len[i][i+1],...,ans+s[j]−len[i][j]} ,因为对于每个t,满足i+1≤t≤j ,必然有len(i,t)+st−s[j]≤ans,也即st≤ans−len(i,t)+s[j] s同时满足这两个条件的j才能由i进行转移. 如果最后dp[k]被更新过,那么限制M就是可行的,否则不行.
参考代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define clr(a,v) memset(a,v,sizeof(a)) 4 typedef long long ll; 5 typedef pair<ll,int> pli; 6 const ll INF=0x3f3f3f3f3f3f3f3fll; 7 const int maxn=1010; 8 const int maxm=5010; 9 int n,m,k,ecnt,head[maxn]; 10 ll s[maxn],p[maxn],t[maxn]; 11 ll dis[maxn][maxn],dp[maxn]; 12 struct Edge{ 13 int v,nxt; 14 ll w; 15 } edge[maxm<<1]; 16 17 void addedge(int u,int v,int w) 18 { 19 edge[ecnt].v=v; 20 edge[ecnt].w=w; 21 edge[ecnt].nxt=head[u]; 22 head[u]=ecnt++; 23 } 24 25 void dijkstra(int id,int ss) 26 { 27 clr(dis[id],INF); dis[id][ss]=0; 28 priority_queue<pli,vector<pli>,greater<pli> > pq; 29 pq.push(make_pair(dis[id][ss],ss)); 30 while(!pq.empty()) 31 { 32 ll tmp=pq.top().first; 33 int u=pq.top().second; pq.pop(); 34 if(dis[id][u]<tmp) continue; 35 for(int i=head[u];~i;i=edge[i].nxt) 36 { 37 int v=edge[i].v; 38 if(dis[id][v]>dis[id][u]+edge[i].w) 39 { 40 dis[id][v]=dis[id][u]+edge[i].w; 41 pq.push(make_pair(dis[id][v],v)); 42 } 43 } 44 } 45 } 46 47 bool check(ll ans) 48 { 49 memset(dp,INF,sizeof(dp[0])*(n+2)); 50 dp[0]=0; 51 for(int i=0;i<k;++i) 52 { 53 ll st=dp[i],mlst=INF,d=0; 54 for(int j=i+1;j<=k;++j) 55 { 56 if(j==i+1) d+=dis[1][p[j]]; 57 else d+=dis[p[j-1]][p[j]]; 58 st=max(st,t[j]);mlst=min(mlst,ans-d+s[j]); 59 ll tt=st+d-s[j]; 60 if(tt<=ans && st<=mlst) dp[j]=min(dp[j],st+d+dis[p[j]][1]); 61 else break; 62 } 63 } 64 return (dp[k]<INF); 65 } 66 67 int main() 68 { 69 scanf("%d%d",&n,&m); 70 int u,v; ll w; 71 memset(head,-1,sizeof(int)*(n+2));ecnt=0; 72 for(int i=1;i<=m;++i) 73 { 74 scanf("%d%d%lld",&u,&v,&w); 75 addedge(u,v,w);addedge(v,u,w); 76 } 77 for(int i=1;i<=n;++i) dijkstra(i,i); 78 scanf("%d",&k); 79 for(int i=1;i<=k;++i) scanf("%lld%lld%lld",&s[i],&p[i],&t[i]); 80 ll l=0,r=INF,mid,ans=0; 81 while(l<=r) 82 { 83 mid=l+r>>1; 84 if(check(mid)) ans=mid,r=mid-1; 85 else l=mid+1; 86 } 87 printf("%lld ",ans); 88 89 return 0; 90 }
题意:我方n人,血量已知,对方m人,血量已知。现在有d点伤害,一点一点扣,每次伤害对所有活着的人的概率相同。问d次伤害后对面全死的概率 题解:首先,因为每个士兵的血量最大为6,且敌我双方的士兵数均为5,因此我们考虑可以用搜索的方法去解决 因为士兵的总血量的状态比较少,因此我们可以考虑用一个12位的long long的每一位去存储每一种血量的个数。每一个12位的ll整型就唯一代表了一种状态。因此我们只需要用记忆化的形式对曾经出现过的结果记进行记录,以达到剪枝的作用。因为我们要记录的是敌军死亡的概率,因此,我们可以优先将敌军的6种血量置于12位ll的高位,这样,当我们访问到的状态值<1000000,则代表已经敌军已经已经死亡,即可直接跳出递归(又一个剪枝)。 最后只需要将相应的概率相乘并相加即为答案。
参考代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m,d; 5 int num[2][10]; 6 map<ll,double> dp; 7 8 ll getnum() 9 { 10 ll ans=0; 11 for(int i=1;i>=0;--i) 12 { 13 for(int j=6;j>=1;--j) ans=ans*10+num[i][j]; 14 } 15 return ans; 16 } 17 18 double dfs(ll nm,int limit) 19 { 20 if(dp.count(nm)) return dp[nm]; 21 //if(!limit) return 0; 22 if(nm<1000000) return 1; 23 if(!limit) return 0; 24 double ans=0; 25 int cnt=0; 26 for(int i=0;i<2;++i) 27 for(int j=1;j<=6;++j) cnt+=num[i][j]; 28 //cout<<cnt<<endl; 29 for(int i=0;i<2;++i) 30 { 31 for(int j=1;j<=6;++j) 32 { 33 if(!num[i][j]) continue; 34 num[i][j]--;num[i][j-1]++; 35 ll k=getnum(); 36 double res=dfs(k,limit-1); 37 dp[k]=res; 38 num[i][j]++;num[i][j-1]--; 39 ans=ans+1.0*num[i][j]/cnt*res; 40 } 41 } 42 return ans; 43 } 44 45 int main() 46 { 47 scanf("%d%d%d",&n,&m,&d); 48 int x; 49 memset(num,0,sizeof(num)); 50 for(int i=1;i<=n;++i) scanf("%d",&x),num[0][x]++; 51 for(int i=1;i<=m;++i) scanf("%d",&x),num[1][x]++; 52 double ans=dfs(getnum(),d); 53 printf("%.8lf ",ans); 54 55 56 return 0; 57 }
F Firing the phaser
unsolved.
G Game Scheduling
题目大意
有m支队伍,每支队伍有n个选手,让所有的选手和其他组的选手进行一次较量.问怎么安排可以让所有的选手在一轮
中只出现一次.且最多一轮不出现.
解题思路
本题是上述的染色算法的一个简单应用,当两个颜色之间还存在着可以染的颜色时,直接染上即可.当两条边之间不
存在可以染的颜色时,则其中一个点必然是一条cdx路径的末端点,故将那条路径翻转,释放出一种新的颜色即可
参考代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef pair<int,int> pii; 4 const int sz=26*26; 5 int match[sz][sz]; 6 bool vis[sz]; 7 bool col[sz]; 8 int n,m; 9 int get_block(int x) 10 { 11 memset(col,0,sizeof(col)); 12 for(int i=0;i<n*m;i++) col[match[x][i]]=1; 13 for(int i=1;i<n*m;i++) if(!col[i]) return i; 14 return n*m; 15 } 16 int check_conflict(int x,int loc) 17 { 18 for(int i=0;i<n*m;i++) if(match[x][i]==loc) return i; 19 return n*m; 20 } 21 void recol(int x, int y) 22 { 23 int pre_match=get_block(y); 24 int conflict=check_conflict(x,pre_match); 25 memset(vis,0,sizeof(vis)); 26 vis[y] = 1; 27 vector<pii> match_line; 28 match_line.push_back(pii(y,pre_match)); 29 while (conflict!=n*m && !vis[conflict]) { 30 vis[conflict] = 1; 31 y=conflict; 32 pre_match = get_block(y); 33 match_line.push_back(pii(y,pre_match)); 34 conflict = check_conflict(x, pre_match); 35 } 36 if (conflict==n*m) { 37 for(auto t:match_line) { 38 match[x][t.first] = t.second; 39 match[t.first][x] = t.second; 40 } 41 } else { 42 int pre_match_x = get_block(x); 43 int conflict_x=check_conflict(conflict,pre_match_x); 44 match[x][conflict] = pre_match_x; 45 match[conflict][x] = pre_match_x; 46 while (conflict_x!=n*m) { 47 int temp= check_conflict(conflict_x,pre_match); 48 match[conflict][conflict_x] = pre_match; 49 match[conflict_x][conflict] = pre_match; 50 conflict=conflict_x; 51 conflict_x=temp; 52 swap(pre_match_x,pre_match); 53 } 54 recol(x,match_line[0].first); 55 } 56 } 57 int main() 58 { 59 scanf("%d%d",&n,&m); 60 for(int i=0;i<n*m;i++) 61 { 62 for(int j=0;j<n*m;j++) 63 { 64 if(i/n!=j/n&&!match[i][j]) 65 recol(i,j); 66 } 67 } 68 int tot=n*(m-1)*n*m/2; 69 for(int k=1;tot;k++) 70 { 71 for(int i=0;i<n*m;i++) 72 { 73 for(int j=i+1;j<n*m;j++) 74 { 75 if(match[i][j]==k) 76 { 77 tot--; 78 printf("%c%d-%c%d ",'A'+i/n,i%n+1,'A'+j/n,j%n+1); 79 } 80 } 81 } 82 cout<<endl; 83 } 84 }
H House Lawn
参考代码:
1 #include<bits/stdc++.h> 2 #define db double 3 #define ll long long 4 using namespace std; 5 const int maxn=1e6+6,inf=1e9; 6 char str[1005]; 7 struct node 8 { 9 char s[65]; 10 int id,p; 11 bool operator<(const node&t)const{return p==t.p? id<t.id:p<t.p;} 12 }a[105]; 13 void gao(int cur,int &pri,int &c,int &t,int &r) 14 { 15 int n=strlen(str),i=0; 16 pri=c=t=r=0; 17 while(str[i]!=',')i++; 18 i++; 19 while(str[i]!=',') 20 pri=pri*10+str[i++]-'0';i++; 21 while(str[i]!=',') 22 c=c*10+str[i++]-'0';i++; 23 while(str[i]!=',') 24 t=t*10+str[i++]-'0';i++; 25 while(i<n) 26 r=r*10+str[i++]-'0'; 27 } 28 int main() 29 { 30 int e,m,pri,c,t,r,v,n=10080,cnt=0; 31 cin>>e>>m; 32 getchar(); 33 for(int i=1;i<=m;i++) 34 { 35 gets(str); 36 gao(i,pri,c,t,r); 37 v=n/(t+r)*t*c; 38 if(v<e) 39 { 40 int tmp=n%(t+r); 41 db v1=(db(e)-v)/c,v2=tmp-v1; 42 if(v1<=(db)t) 43 if(v1*r<=v2*t)v=e; 44 } 45 if(v>=e) 46 { 47 a[++cnt].p=pri; a[cnt].id=i; 48 int len=strlen(str),j=0; 49 while(str[j]!=',') 50 a[cnt].s[j]=str[j],j++; 51 a[cnt].s[j]='