指望得分:\(50 + 20 + 0 = 70\)
實際得分:\(50 + 5 + 0 = 55\)node
大神\(zhx\):考試要先打暴力.ios
因而我困在\(T2\)的\(20\)分暴力中沒法自拔。。調\(T2\)暴力花了一個小時……其實吧。。我想寫\(T3\),然而連怎麼建這棵樹都不會,因此就肝\(T2\)的\(20\)分一直到死。。\(T1\)是個沙比題,人均\(100\)分,這場考試人均\(100+\),我就是倒着數的那個。。內心盡是失落,又實在不會這些題。。沒救了git
\(50\)分:spa
直接\(n^2\)暴力找度爲\(1\)的點,每次找到就將這個點和它連邊的點度都減\(1\),而後把答案存下來最後輸出(代碼中的\(sub1\))debug
另外的\(10\)分:code
由於保證了\(u==1\),因此咱們能夠直接輸出\(n-2\)個\(1\)隊列
\(100\)分:get
考慮用一個優先隊列,把度爲\(1\)的結點的編號放入隊列中,保證每次取出的編號最小,而後直接枚舉這些點的連邊,讓連邊的點度數\(--\),若是此時度數變爲\(1\)則入隊,若是此時度數大於等於\(1\)則輸出這個點,這樣就作完了string
#include <queue> #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 5e5 + 11; const int B = 1e6 + 11; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48); return x * f; } int n, m; struct node { int to, nxt; } e[A]; bool flag = 1; int cnt = 0, tot = 0, head[A], du[A], ans[A], vis[A]; priority_queue <int, vector<int>, greater<int> > Q; inline void add(int from, int to) { e[++cnt].to = to; e[cnt].nxt = head[from]; head[from] = cnt; } namespace sub1 { void solve(int id) { vis[id] = 1; int to = e[head[id]].to; if(!vis[to]) { du[to]--; if(du[to] == 0) vis[to] = 1; ans[++tot] = to; head[id] = e[head[id]].nxt; } else { while(vis[to] && to != 0) { head[id] = e[head[id]].nxt; to = e[head[id]].to; } ans[++tot] = to; if(du[to] == 0) vis[to] = 1; du[to]--; head[id] = e[head[id]].nxt; } return; } void Main() { vis[0] = 1; for(int i = 1; i <= n - 2; i++) for(int j = 1; j <= n; j++) if(du[j] == 1) { du[j]--; solve(j); break; } for(int i = 1; i <= tot; i++) cout << ans[i] << " "; return; } } int main() { freopen("prufer.in", "r", stdin); freopen("prufer.out", "w", stdout); n = read(); for(int i = 1, u, v; i < n; i++) { u = read(), v = read(); add(u, v), add(v, u); if(u != 1) flag = 0; du[u]++, du[v]++; } if(n <= 3000) return sub1::Main(), 0; if(flag) { cout << 1; for(int i = 2; i <= n - 2; i++) cout << " 1"; puts(""); return 0; } for(int i = 1; i <= n; i++) if(du[i] == 1) Q.push(i); for(int i = 1; i <= n; i++) { int u = Q.top(); Q.pop(); for(int j = head[u]; j; j = e[j].nxt) { int to = e[j].to; du[to]--; if(du[to] == 1) Q.push(to); if(du[to] >= 1) cout << to << " "; } } puts(""); return 0; } /* 6 1 3 1 5 3 2 3 6 5 4 3 5 1 3 */
\(20\)分:it
暴搜,只能從左往右搜,因此記錄一下上次用的是哪個,此次枚舉直接從上次用的下一個開始枚舉,保證從左往右
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define int long long using namespace std; const int A = 1e5 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48); return x * f; } int n, m, ans, vis[A], a[A]; char s[A]; int c[4000][4000]; void dfs(int cnt, int last) { if(cnt > n + 1) return; if((cnt - 1) % 2 == 0 && cnt - 1 != 0) { int now = cnt - 1; int zuo = 0, you = 0, cao = 0; for(int i = 1; i <= now / 2; i++) { if(a[i] == 2) { cao = 1; break; } if(a[i] == 1) zuo++; } for(int i = now / 2 + 1; i <= now; i++) { if(a[i] == 1) { cao = 1; break; } if(a[i] == 2) you++; } if(!cao && zuo == you && zuo + you == now) ans++; } for(int i = last + 1; i <= n; i++) { if(!vis[i]) { vis[i] = 1; a[cnt] = (s[i] == '(' ? 1: 2); dfs(cnt + 1, i); vis[i] = 0; } } } signed main() { freopen("beauty.in", "r", stdin); freopen("beauty.out", "w", stdout); scanf("%s", s + 1); n = strlen(s + 1); if(n <= 20) { dfs(1, 0); cout << ans % mod << '\n'; return 0; } return 0; }
\(50\)分:
考慮每一個左括號,不包括他的,左邊有多少個左括號,右邊有多少個右括號,就能夠得出,對於每個左括號的位置,都有:(\(x\)是指左邊不包括這個左括號有多少個左括號,\(y\)是右邊有多少個右括號)
\[\sum_{i = 0}^{x} C(x, i) * C(y, i + 1)\]
而後就有\(50\)了
\(100\)分:
考慮直接換一種想法,咱們枚舉包括這個位置的左括號的,左邊有多少個左括號,右邊有多少個右括號,這個位置必須選,那麼就能得出
\[\sum_{i = 1}^{x} C(x, i) * C(y, i)\]
可是這樣就會把不選這個位置的狀況算上,因此還要減去
\[\sum_{i = 1}^{x} C(x - 1, i) * C(y, i)\]
就得出了
\[\sum_{i = 1}^{x} C(x, i) * C(y, i) - \sum_{i = 1}^{x} C(x - 1, i) * C(y, i) \]
有一個輔助式子
\[\sum_{i = 0}^{x} C(x, i) * C(y, i) = C(x + y, x)\]
因此上面的式子就能寫成
\[(C(x + y, x) - 1 )- ( C(x + y - 1, x - 1) - 1)\]
就等於
\[C(x + y, x) - C(x + y - 1, x - 1) \]
對於每一個左括號的位置,咱們都這樣計算一遍,而後就作完了
時間複雜度\(O(n)\)
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define int long long using namespace std; const int A = 5e5 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48); return x * f; } int n, m, a[A], b[A], fac[A], inv[A], ans; //a[i]左邊的左括號個數,b[i]右邊的右括號個數 char s[A]; int power(int a, int b, int res = 1) { while(b) { if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } void prepare(int n) { fac[0] = 1; for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod; inv[n] = power(fac[n], mod - 2); for(int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod; return; } int C(int n, int m) { if(n < m) return 0; return fac[n] % mod * inv[n - m] % mod * inv[m] % mod; } signed main() { scanf("%s", s + 1); n = strlen(s + 1); prepare(n * 2); for(int i = n; i >= 1; i--) if(s[i] == ')') b[i] = b[i + 1] + 1; else b[i] = b[i + 1]; for(int i = 1; i <= n; i++) { if(s[i] == '(') a[i] = a[i - 1] + 1; else a[i] = a[i - 1]; } for(int i = 1; i <= n; i++) { if(s[i] == ')') continue; int x = a[i], y = b[i]; ans += C(x + y, x) - C(x + y - 1, x - 1) , ans %= mod; } ans = (ans % mod + mod) % mod; cout << ans << '\n'; return 0; }
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; //var int n,m; //Trie struct Tree { int ch[26]; int fail; } T[400010]; int tot; int pos[400010];//index->node void insert(char* st,int num) { int now=0,len=strlen(st); for(int i=0; i<len; i++) { if(!T[now].ch[st[i]-'a']) T[now].ch[st[i]-'a']=++tot; now=T[now].ch[st[i]-'a']; } pos[num]=now; return ; } //fail-tree vector<int>son[400010]; //AC Automaton queue<int>q; void bfs() { for(int i=0; i<26; i++) if(T[0].ch[i]) { q.push(T[0].ch[i]); T[T[0].ch[i]].fail=0; } while(!q.empty()) { int u=q.front(); q.pop(); son[T[u].fail].push_back(u); for(int i=0; i<26; i++) if(T[u].ch[i]) { T[T[u].ch[i]].fail=T[T[u].fail].ch[i]; q.push(T[u].ch[i]); } else T[u].ch[i]=T[T[u].fail].ch[i]; } return ; } //get dfn int dfn[400010],to[400010],now; //vector<char>Vec; void dfs(int u) { dfn[u]=++now; for(int i=0; i<son[u].size(); i++) dfs(son[u][i]); to[u]=now; return ; } /* //for debug void _dfs(int u) { printf("%d: str=",u); for(int i=0;i<Vec.size();i++) printf("%c",Vec[i]); printf("\n"); for(int i=0;i<26;i++) if(T[u].ch[i]){ Vec.push_back(i+'a'); _dfs(T[u].ch[i]); Vec.pop_back(); } return ; }*/ //Fenwick int c[400010]; int lowbit(int x) { return x&(-x); } void add(int x,int y) { for(; x<=tot+1; x+=lowbit(x)) c[x]+=y; return ; } int query(int x) { int ans=0; for(; x; x-=lowbit(x)) ans+=c[x]; return ans; } //Song-Tree vector<pair<int,char> >S[400010]; //queries vector<int>qnum[400010]; //answers int ans[400010]; //REAL-DFS void DFS(int u,int state) { add(dfn[state],1); for(int i=0; i<qnum[u].size(); i++) { int v=qnum[u][i]; ans[v]=query(to[pos[v]])-query(dfn[pos[v]]-1); } for(int i=0; i<S[u].size(); i++) { int v=S[u][i].first; int C=S[u][i].second-'a'; DFS(v,T[state].ch[C]); } add(dfn[state],-1); return ; } char str[400010]; int main() { freopen("string.in","r",stdin); freopen("string.out","w",stdout); scanf("%d",&n); for(int i=1; i<=n; i++) { int op,fa; scanf("%d",&op); if(op==1)fa=0; else scanf("%d",&fa); scanf("%s",str); S[fa].push_back(make_pair(i,str[0])); } scanf("%d",&m); for(int i=1; i<=m; i++) { int u; scanf("%d",&u); scanf("%s",str); insert(str,i); qnum[u].push_back(i); } // _dfs(0); bfs(); dfs(0); DFS(0,0); // for(int i=1;i<=tot;i++) // printf("fail[%d]=%d\n",i,T[i].fail); for(int i=1; i<=m; i++) printf("%d\n",ans[i]); // for(int i=1;i<=m;i++) // printf("%d ",pos[i]); // printf("\n"); return 0; }