这次写day2的总结
T1:表达式
题面:给你一串表达式
在本题中,我们对合法表达式定义如下:
1. 任何连续(至少1个)数字是合法表达式;
2. 若x是合法表达式,则(x)也是合法表达式;
3. 若x和y 是合法表达式,则x+y、x-y、x*y、x/y都是合法表达式;
4. 若x是合法表达式,则在x 前后添加任意数量的空白符也是合法表达式。
现在给你若干个表达式,请你判断这些表达式是否是合法的。
emmmm,写一个类似于区间dp的东西就行啦(的确定复合NOIPday2T2的难度的,但是好麻烦的感觉)
emmmmm自己没有来得及改自己的wa程序,借用学长的ac程序
#include <bits/stdc++.h> using namespace std; #define ll long long #define up(i,j,n) for (int i = j; i <= n; i++) #define down(i,j,n) for (int i = j; i >= n; i--) #define cmax(a,b) a = max (a, b) #define cmin(a,b) a = min (a, b) #define FILE "expr" const int MAXN = 55; const int oo = 0x3f3f3f3f; int T, N; char s[MAXN]; int vaild[MAXN][MAXN]; bool isop(char o){ if (o == '+') return 1; if (o == '-') return 1; if (o == '*') return 1; if (o == '/') return 1; return 0; } bool allempty(int le, int ri){ up (i, le, ri) if (s[i] != ' ') return 0; return 1; } bool chk(int le, int ri){ if (vaild[le][ri] != -1) return vaild[le][ri]; if (le > ri) return vaild[le][ri] = 0; if (allempty(le, ri)) return vaild[le][ri] = 0; vaild[le][ri] = 0; bool allnumber = 1; up (i, le, ri) if (s[i] < '0' || s[i] > '9') { allnumber = 0; break; } if (allnumber) return vaild[le][ri] = 1; if (le == ri) return vaild[le][ri] = 0; if (s[le] == '(' && s[ri] == ')') return vaild[le][ri] = chk(le + 1, ri - 1); bool ok[MAXN]; up (i, le, ri) ok[i] = 0; up (i, le, ri) if (isop(s[i])) { if (chk(le, i - 1)) ok[i - 1] |= 1; up (j, le + 1, i - 1) if (isop(s[j]) && ok[j - 1]) ok[i - 1] |= chk(j + 1, i - 1); } up (i, le + 1, ri) if (isop(s[i]) && ok[i - 1]) vaild[le][ri] |= chk(i + 1, ri); int lower = le - 1, upper = ri + 1; while (s[lower + 1] == ' ') lower++; while (s[upper - 1] == ' ') upper--; vaild[le][ri] |= chk(lower + 1, upper - 1); return vaild[le][ri]; } int main(){ freopen(FILE".in", "r", stdin); freopen(FILE".out", "w", stdout); scanf("%d", &T); char ch = getchar(); while (T--) { gets(s); N = strlen(s); up (i, 0, N - 1) up (j, i, N - 1) vaild[i][j] = -1; puts(chk(0, N - 1) ? "Yes" : "No"); } return 0; }
T2:食物链
emmmmm省选题还行
题面:给你一些捕食关系,求有多少条食物链
emmmm一A?类似于拓扑序的东西,我把每一个当前入度为零的点所连的边的权设成1,每次去边就往下传递值,最后一个点就是答案。
#include<bits/stdc++.h> #define mode 1000000007 using namespace std; map<string,int> a; int m; struct node { int y; int next; }b[300009]; int lv[100009]; int n; int sum; int f[100009]; int op[100009]; int vl[100009]; int q[100009]; int num; void init() { cin>>m; memset(lv,0,sizeof(lv)); memset(op,0,sizeof(op)); for(int i=1;i<=m;i++) { string x; string y; cin>>x>>y; if(a[x]==0) a[x]=++n; if(a[y]==0) a[y]=++n; b[++sum].y=a[y]; b[sum].next=f[a[x]]; f[a[x]]=sum; lv[a[y]]++; vl[a[x]]++; } num=n; } void topsort(int x) { q[1]=x; op[x]=1; int tou=0,wei=1; while(tou<wei) { tou++; int t=q[tou]; for(int i=f[t],y;i;i=b[i].next) { lv[y=b[i].y]--; op[y]=(op[y]+op[t])%mode; if(lv[y]==0)q[++wei]=y; } } cout<<op[wei]<<endl; } int main() { freopen("chain.in","r",stdin); freopen("chain.out","w",stdout); init(); for(int i=1;i<=n;i++) if(lv[i]==0)b[++sum].y=i,b[sum].next=f[n+1],f[n+1]=sum,lv[i]++; for(int i=1;i<=n;i++) if(vl[i]==0)b[++sum].y=n+2,b[sum].next=f[i],f[i]=sum,lv[n+2]++; topsort(n+1); int yu=0; /*for(int i=1;i<=n;i++) if(vl[i]==0)yu=(op[i]+yu)%mode; cout<<yu<<endl;*/ }
T3:emmmm学长说是出烂的题了
题面:从(0,0)走到(n,m)有多少种走法,其中有k个点是坏点,不能通过。
山神讲过的题,把每个坏点排序,x为第一关键字,y第二关键字,从小到大,
设dp[i]表示到第i个坏点且不经过其他坏点的总方案数,新增加一个点(n,m),则dp[k]为所求答案。
则有状态转移方程dp[i] = C[x[i]+y[i],x[i]) - sum(dp[j] * C(x[i] - x[j] + y[i] - y[j],x[i] - x[j])
那么问题就是快速求组合数了,容我智障,数论0基础,不会求
学长教了一种o(n)预处理,o(1)查询的求组合数的方法。
int C(int a, int b){ if (a < 0 || b < 0 || a < b) return 0; return mul(fac[a], mul(inv[b], inv[a - b])); } void Prepare(){ scanf("%d%d%d", &N, &M, &K); up (i, 1, K) scanf("%d%d", &a[i].fi, &a[i].se); a[++K] = make_pair(N, M); sort(a + 1, a + K + 1); fac[0] = 1; inv[1] = 1; inv[0] = 1; up (i, 1, LIM) fac[i] = mul (i, fac[i - 1]); up (i, 2, LIM) inv[i] = mul (mod - mod / i, inv[mod % i]); up (i, 1, LIM) cmul(inv[i], inv[i - 1]); }