[SinGuLaRiTy] 2017 百度之星程序设计大赛 复赛
【SinGuLaRiTy-1038】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.
Arithmetic of Bomb
Problem Description
众所周知,度度熊非常喜欢数字。
它最近在学习小学算术,第一次发现这个世界上居然存在两位数,三位数……甚至N位数!
但是这回的算术题可并不简单,由于含有表示bomb的#号,度度熊称之为 Arithmetic of Bomb。
Bomb Number中的bomb,也就是#号,会展开一些数字,这会导致最终展开的数字超出了度度熊所能理解的范畴。比如”(1)#(3)”表示”1”出现了3次,将会被展开为”111”,
同理,”(12)#(2)4(2)#(3)”将会被展开为”12124222”。
为了方便理解,下面给出了Bomb Number的BNF表示。
```
<bomb number> := <bomb term> | <bomb number> <bomb term>
<bomb term> := <number> | '(' <number> ')' '#' '(' <non-zero-digit> ')'
<number> := <digit> | <digit> <number>
<digit> := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
<non-zero-digit> := '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
```
请将Bomb Number中所有的#号展开,由于数字可能很长,结果对 1 000 000 007 取模。
Input
第一行为T,表示输入数据组数。
每组数据包含一个Bomb Expression。
- 1≤T≤100
- 1≤length(Bomb Number)≤1000
Output
对每组数据输出表达式的结果,结果对 1 000 000 007 取模。
Sample Input
4
1
(1)#(3)
(12)#(2)4(2)#(3)
(12)#(5)
Sample Output
1
111
12124222
212121205
Code
模拟,签到题不解释。
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #define ll long long #define inf 0x3f3f3f3f #define maxn 101000 #define MOD 1000000007 using namespace std; int read() { char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } int n; char s[maxn],ans[maxn],nows[maxn]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%s",s+1); int n=strlen(s+1); int nowstep=0,len=0,tot=0; for(int i=1;i<=n;i++)if(isdigit(s[i])) { if(nowstep==1){nows[++len]=s[i];} else if(nowstep==2) { for(int j=1;j<=s[i]-'0';j++) for(int k=1;k<=len;k++) ans[++tot]=nows[k]; len=0;nowstep=0; } else if(nowstep==0)ans[++tot]=s[i]; } else { if(nowstep==0&&s[i]=='(') nowstep=1; else if(nowstep==1&&s[i]==')') nowstep=2; } long long ansnum=0; for(int i=1;i<=tot;i++) ansnum=(ansnum*10+ans[i]-'0')%MOD; printf("%lld\n",ansnum); } return 0; }
Arithmetic of Bomb II
Problem Descroption
众所周知,度度熊非常喜欢数字。
它最近在学习小学算术,沉迷于计算A+B中不能自拔。
但是这回的算术题可并不简单,由于含有表示bomb的#号,度度熊称之为 Arithmetic of Bomb。
Arithmetic of Bomb的目的与普通算术一样,就是计算一些Bomb Expression的结果。比如,”1-2+3”的结果为2。然而,bomb,也就是#号,会展开一些普通表达式,这会导致需要计算的式子超出了度度熊所能理解的范畴。比如”(1-2+3)#(3)”表示”1-2+3”出现了3次,将会被展开为”1-2+31-2+31-2+3”。
为了方便理解,下面给出了Bomb Expression的BNF表示。
```
<bomb expression> := <bomb term> | <bomb expression> <bomb term>
<bomb term> := <bomb statement> | '(' <bomb statement> ')' '#' '(' <number> ')'
<bomb statement> := <bomb element> | <bomb statement> <bomb element>
<bomb element> := <digit> | '+' | '-' | '*'
<normal expression> := <norm term> | <normal expression> '+' <norm term> | <normal expression> '-' <norm term>
<norm term> := <number> | <norm term> '*' <number>
<number> := <digit> | <non-zero-digit> <number>
<digit> := '0' | <non-zero-digit>
<non-zero-digit> := '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
```
请先将Bomb Expression中所有的#号展开,使其成为Normal Expression(题目的输入保证展开后是一个合法的Normal Expression),再来计算这个表达式的结果。
Input
第一行为T,表示输入数据组数。
每组数据包含一个Bomb Expression。
●1≤T≤50
●1≤length(Bomb Statement)≤10
●1≤length(Number in Bomb term)≤10
●1≤length(Bomb Expression)≤300 000
Output
对每组数据输出表达式的结果,结果对 1 000 000 007 取模。
Sample Input
6
1-2+3
(1-2+3)#(3)
(1)#(3)
(1+)#(2)1
(2*3+1)#(2)
(2)#(2)1+1(2)#(2)
Sample Output
2
60
111
3
43
343
Code
我还不会矩阵运算呀,只好先在这里放一放大牛的代码了(能做出这道题的确佩服!)
【本题题解来自于:y5zsq】
Code
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<vector> #include<queue> using namespace std; typedef long long ll; #define maxn 300005 #define mod 1000000007 vector<string>term; vector<ll>num; char c[maxn]; int T; void Deal() { term.clear(),num.clear(); int n=strlen(c); for(int i=0;i<n;) { string s; if(c[i]=='(') { i++; while(c[i]!=')')s.push_back(c[i++]); i+=3; ll t=0; while(c[i]!=')')t=t*10+c[i++]-'0'; if(t)term.push_back(s),num.push_back(t); i++; } else { while(i<n&&c[i]!='(')s.push_back(c[i++]); term.push_back(s),num.push_back(1); } } } typedef ll Mat[4][4]; Mat A,B,C; void Mul(Mat &a,Mat b) { Mat c; for(int i=0;i<4;i++) for(int j=0;j<4;j++) { c[i][j]=0; for(int k=0;k<4;k++) c[i][j]+=a[i][k]*b[k][j]; } for(int i=0;i<4;i++) for(int j=0;j<4;j++) a[i][j]=(c[i][j]%mod+mod)%mod; } void Pow(Mat &a,ll b) { if(b==1)return ; Mat c; for(int i=0;i<4;i++) for(int j=0;j<4;j++) c[i][j]=(i==j); while(b) { if(b&1)Mul(c,a); Mul(a,a); b>>=1; } for(int i=0;i<4;i++) for(int j=0;j<4;j++) a[i][j]=c[i][j]; } ll Count() { for(int i=0;i<4;i++) for(int j=0;j<4;j++) A[i][j]=(i==j); int sign=1; for(int k=0;k<term.size();k++) { string s=term[k]; ll t=num[k]; int n=s.size(); for(int i=0;i<4;i++) for(int j=0;j<4;j++) B[i][j]=(i==j); for(int i=0;i<n;i++) { memset(C,0,sizeof(C)); C[0][0]=C[3][3]=1; if(s[i]>='0'&&s[i]<='9') C[1][1]=10,C[2][1]=s[i]-'0',C[2][2]=1; else if(s[i]=='+') C[1][0]=sign,C[3][2]=1,sign=1; else if(s[i]=='-') C[1][0]=sign,C[3][2]=1,sign=-1; else C[1][2]=1; Mul(B,C); } Mul(A,B); if(t>1) { for(int i=0;i<4;i++) for(int j=0;j<4;j++) B[i][j]=(i==j); for(int i=0;i<n;i++) { memset(C,0,sizeof(C)); C[0][0]=C[3][3]=1; if(s[i]>='0'&&s[i]<='9') C[1][1]=10,C[2][1]=s[i]-'0',C[2][2]=1; else if(s[i]=='+') C[1][0]=sign,C[3][2]=1,sign=1; else if(s[i]=='-') C[1][0]=sign,C[3][2]=1,sign=-1; else C[1][2]=1; Mul(B,C); } Pow(B,t-1); Mul(A,B); } } ll ans=(A[2][0]+A[3][0])%mod; if(sign==1)ans=(ans+A[2][1]+A[3][1])%mod; else ans=(ans-A[2][1]-A[3][1])%mod; ans=(ans+mod)%mod; return ans; } int main() { scanf("%d",&T); while(T--) { scanf("%s",c); Deal(); printf("%I64d\n",Count()); } return 0; }
Pokémon GO
Problem Description
众所周知,度度熊最近沉迷于 Pokémon GO。
今天它决定要抓住所有的精灵球!
为了不让度度熊失望,精灵球已经被事先放置在一个2*N的格子上,每一个格子上都有一个精灵球。度度熊可以选择任意一个格子开始游戏,抓捕格子上的精灵球,然后移动到一个相邻的至少有一个公共点的格子上继续抓捕。例如,(2, 2) 的相邻格子有(1, 1), (2, 1) 和 (1, 2) 等等。
现在度度熊希望知道将所有精灵球都抓到并且步数最少的方案数目。两个方案被认为是不同,当且仅当两个方案至少有一步所在的格子是不同的。
Input
第一行为T,表示输入数据组数。
每组数据包含一个数N。
●1≤T≤100
●1≤N≤10000
Output
对每组数据输出方案数目,结果对 1 000 000 007 取模。
Sample Input
3
1
2
3
Sample Input
2
24
96
Code
动态规划
#include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> #include<iostream> #include<cstring> #define ll long long const int maxn=10010,MOD=1000000007; ll a[maxn],b[maxn],n; int main() { b[1]=1; for(int i=2;i<=maxn;i++) b[i]=(b[i-1]*2)%MOD; a[1]=1; a[2]=6; for(int i=3;i<=maxn;i++) a[i]=(2*a[i-1]+b[i]+4*a[i-2])%MOD; int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); ll ans=0; for(int i=2;i<=n-1;i++) ans=(ans+16*b[i-1]%MOD*a[n-i])%MOD; ans=(ans+4*a[n])%MOD; if(n==1) ans=2; printf("%lld\n",ans); } return 0; }
Pokémon GO II
Problem Description
众所周知,度度熊最近沉迷于 Pokémon GO。
由于太过沉迷,现在它只能按照游戏内置的指令行走了:对,简直就像一个现实中的Pokémon!
游戏内置的指令实际上可以抽象成一种:保持现在的朝向前行X米,然后右转。度度熊相信,只要遵循这个指令,它就一定可以抓到最珍奇的精灵球。
但不幸的是,这个指令并不是很有可信度,有时会引导度度熊走回原来的位置。现在它想知道,在第几条指令时它第一次回到已经走过的位置?如果这种情况没有发生,请输出 “Catch you”。
Input
第一行为T,表示输入数据组数。
每组数据的第一行包含一个数N,表示指令长度。接着的一行包含N个数字Xi,表示第i个指令中前行的距离。
● 1≤T≤100
● 1≤N≤1 000 000
● 1≤Xi≤1 000 000 000
Output
对每组数据输出第一次回到已经走过的位置时的指令下标i (1≤i≤N)。
如果这种情况没有发生,请输出 “Catch you”。
Sample Input
3
4
2 2 2 2
4
2 1 3 1
5
2 1 3 1 3
Sample Output
4
Catch you
5
Code
目测几何神犇题,发现可以画画图找规律......第一次覆盖一定发生在轨迹的前8段中。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cmath> #include<iostream> #include<cstdlib> #define MAXN 1000010 using namespace std; const int dx[]={0,1,0,-1},dy[]={1,0,-1,0}; int N,a[MAXN]; struct Point { int x,y; Point(int _x=0,int _y=0):x(_x),y(_y){} }points[MAXN]; bool overlap(int a,int b,int c,int d) { if(a>b) swap(a, b); if(c>d) swap(c, d); return !(b<c||d<a); } bool intersect(Point a1,Point a2,Point b1,Point b2) { bool is_vertical_a=(a1.x==a2.x),is_vertical_b=(b1.x==b2.x); if(is_vertical_a && is_vertical_b) return a1.x==b1.x&&overlap(a1.y,a2.y,b1.y,b2.y); if(!is_vertical_a && !is_vertical_b) return a1.y==b1.y&&overlap(a1.x,a2.x,b1.x,b2.x); if(is_vertical_a) { swap(a1,b1); swap(a2,b2); } return !(max(a1.x,a2.x)<b1.x||min(a1.x,a2.x)>b1.x||max(b1.y,b2.y)<a1.y||min(b1.y,b2.y)>a1.y); } int solve() { int x=0,y=0; for(int i=0;i<N;++i) { x+=dx[i&3]*a[i]; y+=dy[i&3]*a[i]; points[i+1].x=x; points[i+1].y=y; for(int j=max(0,i-8);j<i-2;++j) if(intersect(points[j],points[j+1],points[i],points[i+1])) return i; } return -1; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d",&N); for(int i=0;i<N;++i) scanf("%d",a+i); int result=solve(); if(result==-1) printf("Catch you\n"); else printf("%d\n", result+1); } return 0; }
Valley Numer
Problem Description
众所周知,度度熊非常喜欢数字。
它最近发明了一种新的数字:Valley Number,像山谷一样的数字。
当一个数字,从左到右依次看过去数字没有出现先递增接着递减的“山峰”现象,就被称作 Valley Number。它可以递增,也可以递减,还可以先递减再递增。在递增或递减的过程中可以出现相等的情况。
比如,1,10,12,212,32122都是 Valley Number。
121,12331,21212则不是。
度度熊想知道不大于N的Valley Number数有多少。
注意,前导0是不合法的。
Input
第一行为T,表示输入数据组数。
每组数据包含一个数N。
● 1≤T≤200
● 1≤length(N)≤100
Output
对每组数据输出不大于N的Valley Number个数,结果对 1 000 000 007 取模。
Sample Input
3
3
14
120
Sample Input
Code
记忆化搜索/数位DP
#include<cstdio> #include<algorithm> #include<cstring> #include<cctype> #define ll long long using namespace std; const ll maxn=110,MOD=1000000007; ll f[maxn][2][10],a[maxn],n; char s[maxn]; ll dfs(ll pos,ll state,ll limit,ll pre) { if(pos==-1) { if(~pre) return 1; else return 0; } if(!limit&&~pre&&~f[pos][state][pre]) return f[pos][state][pre]; ll up=limit?a[pos]:9; ll ans=0; for(int i=0;i<=up;i++) { if(pre==-1&&i==0) ans=(ans+dfs(pos-1,state,limit&&i==up,pre))%MOD; else if(pre==-1&&i!=0) ans=(ans+dfs(pos-1,state,limit&&i==up,i))%MOD; else if(state==0) ans=(ans+dfs(pos-1,i>pre,limit&&i==up,i))%MOD; else if(state==1&&i>=pre) ans=(ans+dfs(pos-1,state,limit&&i==up,i))%MOD; } if(!limit&&~pre) f[pos][state][pre]=ans; return ans; } int main() { ll T; scanf("%lld",&T); memset(f,-1,sizeof(f)); while(T--) { scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++) a[n-i]=s[i]-'0'; printf("%lld\n",dfs(n-1,0,1,-1)); } return 0; }
Valley Numer II
Problem Description
众所周知,度度熊非常喜欢图。
它最近发现了图中也是可以出现 valley —— 山谷的,像下面这张图。
为了形成山谷,首先要将一个图的顶点标记为高点或者低点。标记完成后如果一个顶点三元组<X, Y, Z>中,X和Y之间有边,Y与Z之间也有边,同时X和Z是高点,Y是低点,那么它们就构成一个valley。
度度熊想知道一个无向图中最多可以构成多少个valley,一个顶点最多只能出现在一个valley中。
Input
第一行为T,表示输入数据组数。
每组数据的第一行包含三个整数N,M,K,分别表示顶点个数,边的个数,标记为高点的顶点个数。
接着的M行,每行包含两个两个整数Xi,Yi,表示一条无向边。
最后一行包含K个整数Vi,表示这些点被标记为高点,其他点则都为低点。
● 1≤T≤20
● 1≤N≤30
● 1≤M≤N*(N-1)/2
● 0≤K≤min(N,15)
● 1≤Xi, Yi≤N, Xi!=Yi
● 1≤Vi≤N
Output
对每组数据输出最多能构成的valley数目。
Sample Input
3 3 2 2 1 2 1 3 2 3 3 2 2 1 2 1 3 1 2 7 6 5 1 2 1 3 1 4 2 3 2 6 2 7 3 4 5 6 7
Sample Output
1
0
2
Code
状压DP
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #define ll long long using namespace std; int read() { char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } const int inf=0x3f3f3f3f,maxn=40,maxk=80010; int n,f[2][maxk],c,k,m,hi[maxn]; bool Map[maxn][maxn],high[maxn]; void dfs(int dep,int now,int pre,int cyc,int x) { if(dep==3) f[now][c]=max(f[now][c],f[pre][cyc]+1); else for(int i=0;i<=k-1;i++) if(Map[x][hi[i+1]]&&!(c&(1<<i))) { c|=(1<<i); dfs(dep+1,now,pre,cyc,x); c^=(1<<i); } } int main() { int T; scanf("%d",&T); while(T--) { n=read();m=read();k=read(); int u,v; memset(Map,0,sizeof(Map)); for(int i=1;i<=m;i++) { u=read();v=read(); Map[u][v]=Map[v][u]=1; } memset(high,0,sizeof(high)); for(int i=1;i<=k;i++) { u=read(); high[u]=1; } int nowk=0; for(int i=1;i<=n;i++)if(high[i])hi[++nowk]=i; k=nowk; memset(f,0,sizeof(f)); int x=1; for(int i=1;i<=n;i++)if(!high[i]) { x=1-x; memset(f[x],0,sizeof(f[x])); for(int j=0;j<(1<<k);j++) { f[x][j]=max(f[x][j],f[1-x][j]); c=j; dfs(1,x,1-x,j,i); } } int ans=0; for(int i=0;i<(1<<k);i++)ans=max(ans,f[x][i]); printf("%d\n",ans); } return 0; }
Time: 2017-08-24