cf Round 607
A.Chain Reaction(DP+二分)
题意:一排有n个灯塔,每个灯塔给出坐标xi和力量yi,
每次从最右边依次点亮灯塔,每点亮一个灯塔,它左边的距离它yi范围内的灯塔将受到损坏。
现在允许在最右边>max(xi)处添加一座力量值yi的灯塔。
问最少只会损坏多少灯塔。
分析:因为我们是从右边点亮灯塔的,所以我们点亮我们添加的那座灯塔只会损坏连续个灯塔。最后就相当于从末尾的某一个灯塔开始点亮。
我们令dp[i]表示点亮第i座灯塔最少损坏多少灯塔。
于是我们对于每个dp[i],二分可以得到他前面的前驱。
所以dp[i]=dp[j]+1(j<i).
注意题目给出的灯塔并不是有序的,需要排序一遍。
# include <stdio.h> # include <string.h> # include <stdlib.h> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <math.h> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define MAXN 100005 # define eps 1e-5 # define MAXM 1000005 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; typedef unsigned long long ULL; int _MAX(int a, int b){return a>b?a:b;} int _MIN(int a, int b){return a>b?b:a;} int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } int a[100005][2], sum[100005]; int comp(const void * b, const void * c){return *(int *)b-*(int *)c;} int main () { int n; scanf("%d",&n); FOR(i,1,n) scanf("%d%d",&a[i][0],&a[i][1]); qsort(a+1,n,sizeof(a[1]),comp); sum[1]=1; FOR(i,2,n) { int l=0, r=i, mid; while (l<r) { mid=(l+r)>>1; if (l==mid) break; if (a[mid][0]<a[i][0]-a[i][1]) l=mid; else r=mid; } sum[i]=1+sum[l]; } int ans=0; FOR(i,1,n) ans=max(ans,sum[i]); printf("%d\n",n-ans); return 0; }
B.Zuma(区间DP)
给出一串n个数字(n<=500).每次操作可以消掉一个回文串。然后补齐。
问最少需要操作多少次。
姿势很优雅的区间DP;
很容易想到dp[l][r]表示消掉[l,r]的回文串最少需要多少步。
但是在转移的过程中GG了,假如它先消中间的某一部分,然后再对齐再消,这样咋办。
于是我码了一个暴力判断字符串是否回文。结果TLE了。
仔细一想,因为1个数字显然是回文串。
对于[l,r]区间,我们枚举[l+1,r]内的一个k使得a[k]==a[l],这时候我们可以先消[l+1,k-1].
再消a[l]和a[k],我们注意到消[l+1,k-1]的最后一步一定会变成一个回文串,
因为a[l]==a[k], 所以我们消a[l,k]的步数就等于a[l+1,k-1].
即dp[l][k]==dp[l+1][k-1].
所以状态转移方程就是dp[l][r]=min(dp[l+1][r]+1,dp[l+1][k-1]+dp[k+1][r])(a[k]==a[l])
最后判断一下边界就行了。
# include <stdio.h> # include <string.h> # include <stdlib.h> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <math.h> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define MAXN 100005 # define eps 1e-5 # define MAXM 1000005 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; typedef unsigned long long ULL; int _MAX(int a, int b){return a>b?a:b;} int _MIN(int a, int b){return a>b?b:a;} int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } int n, a[505], dp[505][505]; int dfs(int l, int r) { if (~dp[l][r]) return dp[l][r]; if (l>r) return 1; if (l==r) return dp[l][r]=1; int ans=INF; ans=min(ans,dfs(l+1,r)+1); FOR(i,l+1,r) { if (a[i]==a[l]) { if (i==r) ans=min(ans,dfs(l+1,i-1)); else if (i==l+1) ans=min(ans,dfs(i+1,r)+1); else ans=min(ans,dfs(l+1,i-1)+dfs(i+1,r)); } } return dp[l][r]=ans; } int main () { mem(dp,-1); scanf("%d",&n); FOR(i,1,n) scanf("%d",a+i); dfs(1,n); printf("%d\n",dp[1][n]); return 0; }
C.Marbles(脑洞+hash)
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; const LL INF = 1E9+9; const int MI = ~0u>>1; int main() { int n; string s1,s2; cin>>n>>s1>>s2; n--; for(int i=0;i<n;i++) { switch(s2[i]) { case 'N':s2[i]='S';break; case 'S':s2[i]='N';break; case 'W':s2[i]='E';break; case 'E':s2[i]='W';break; } } ULL v1=0,v2=0; const ULL seed=131; ULL wt=1; for(int i=n-1;i>=0;i--,wt*=131) { v1=v1+(s1[i]-'a')*wt; v2=v2*seed+s2[i]-'a'; if(v1==v2) { printf("NO"); return 0; } } printf("YES"); return 0; }
D.Power Tree(线段树维护dfn+逆元)
分析: 我们可以算出每一个节点对根节点的贡献因子mi。
即di是节点i的儿子节点数+1.
实际上mi就是从i到根节点的简单路径上的di的乘积。
当新加入一个节点u到节点p时。我们观察可以发现,仅仅改变了p和p的子树的mi。
显然是mi=mi*(dp+1)/dp. mu=mp。
我们需要一个数据结构可以 对一个区间进行乘,更新一个值,以及求一个区间的总和。
我们用线段树维护dfn序。
那么一个节点的子树就是一段连续的区间。
我们求一个节点的贡献时。显然就是sum同时除以他们的公共路径的mi。
由于乘法是一个浮点数。我们先累乘所有的分子,再累乘所有的分母,最后我们用乘法逆元
可以求出取模。
代码量有点受不了。。。
#include <cstdio> #include <vector> #include <algorithm> #pragma comment(linker, "/STACK:16000000") using namespace std; typedef long long ll; const int mod = 1000000007; const int Maxn = 200005; const int Maxm = 1048576; int n, q; int val[Maxn]; int a[Maxn], b[Maxn], c[Maxn]; vector <int> neigh[Maxn]; int ch[Maxn]; int P[Maxn], cur, L[Maxn], R[Maxn]; int mult[Maxm]; int fl[Maxm], sum[Maxm]; void Traverse(int v) { L[v] = ++cur; for (int i = 0; i < neigh[v].size(); i++) Traverse(neigh[v][i]); R[v] = cur; } void createMult(int v, int l, int r) { mult[v] = 1; if (l < r) { int m = l + r >> 1; createMult(2 * v, l, m); createMult(2 * v + 1, m + 1, r); } } void updMult(int v, int l, int r, int a, int b, int val) { if (l == a && r == b) mult[v] = ll(mult[v]) * val % mod; else { int m = l + r >> 1; if (a <= m) updMult(2 * v, l, m, a, min(m, b), val); if (m + 1 <= b) updMult(2 * v + 1, m + 1, r, max(m + 1, a), b, val); } } int getMult(int v, int l, int r, int x) { int res = mult[v]; if (l < r) { int m = l + r >> 1; if (x <= m) res = ll(res) * getMult(2 * v, l, m, x) % mod; else res = ll(res) * getMult(2 * v + 1, m + 1, r, x) % mod; } return res; } void Union(int v) { sum[v] = (sum[2 * v] + sum[2 * v + 1]) % mod; } void downOn(int v, int val) { fl[v] = ll(fl[v]) * val % mod; sum[v] = ll(sum[v]) * val % mod; } void Down(int v) { if (fl[v] != 1) { downOn(2 * v, fl[v]); downOn(2 * v + 1, fl[v]); fl[v] = 1; } } void createSum(int v, int l, int r) { fl[v] = 1; if (l == r) sum[v] = val[l]; else { int m = l + r >> 1; createSum(2 * v, l, m); createSum(2 * v + 1, m + 1, r); Union(v); } } int getSum(int v, int l, int r, int a, int b) { if (l == a && r == b) return sum[v]; else { Down(v); int m = l + r >> 1; int res = 0; if (a <= m) res = (res + getSum(2 * v, l, m, a, min(m, b))) % mod; if (m + 1 <= b) res = (res + getSum(2 * v + 1, m + 1, r, max(m + 1, a), b)) % mod; return res; } } void multSum(int v, int l, int r, int a, int b, int val) { if (l == a && r == b) downOn(v, val); else { Down(v); int m = l + r >> 1; if (a <= m) multSum(2 * v, l, m, a, min(m, b), val); if (m + 1 <= b) multSum(2 * v + 1, m + 1, r, max(m + 1, a), b, val); Union(v); } } void addSum(int v, int l, int r, int x, int val) { if (l == r) sum[v] = val; else { Down(v); int m = l + r >> 1; if (x <= m) addSum(2 * v, l, m, x, val); else addSum(2 * v + 1, m + 1, r, x, val); Union(v); } } int Inv(int a) { int p = mod - 2; int res = 1; while (p) { if (p & 1) res = ll(res) * a % mod; p >>= 1; a = ll(a) * a % mod; } return res; } int main() { n = 1; scanf("%d %d", &val[n], &q); for (int i = 0; i < q; i++) { scanf("%d %d", &a[i], &b[i]); if (a[i] == 1) { scanf("%d", &c[i]); n++; P[n] = b[i]; neigh[b[i]].push_back(n); } } Traverse(1); createMult(1, 1, R[1]); createSum(1, 1, R[1]); n = 1; for (int i = 0; i < q; i++) if (a[i] == 1) { n++; ch[b[i]]++; int tomult = ll(ch[b[i]] + 1) * Inv(ch[b[i]]) % mod; updMult(1, 1, R[1], L[b[i]], R[b[i]], tomult); multSum(1, 1, R[1], L[b[i]], R[b[i]], tomult); val[n] = c[i]; tomult = getMult(1, 1, R[1], L[n]); addSum(1, 1, R[1], L[n], ll(tomult) * val[n] % mod); } else { int res = getSum(1, 1, R[1], L[b[i]], R[b[i]]); if (P[b[i]]) { int tomult = Inv(getMult(1, 1, R[1], L[P[b[i]]])); res = ll(res) * tomult % mod; } printf("%d\n", res); } return 0; }
E.Cross Sum(待填坑)