牛牛的迷宫2
思路:首先我们可以很模糊的联想到如果按照一下递归下去
BD
RBD
RBD
RB
会发现每个B沿着下去会像二进制一样,1 2 4 8....,所以我们发现只要我们的矩阵的对角线按这种模式建找就行;
因为二进制是可以表示所有数字的,所以我们只需在每一行建造三个B,然后左下角全为D,
最后一行R表示向右到终点,所以,只要这个K的数字的二进制这个位为0,那么我们对应得这一行得第一个B给改为R
不给“流下来”到终点。
#includeusing namespace std; const int mod=1e9+7; int i,i0,n,m,ans,k; char mp[55][55]; int main() { n=32,m=30; scanf("%d",&k); mp[n][m]='B'; for(int i0=1;i0<=m;i0++)mp[1][i0]=(i0<=2)?'B':'R'; for(int i=2;i<=n-1;i++) { for(int i0=1;i0<=m;i0++) { if(i0
F
静态区间和
F 第一种: #includeusing namespace std; const int MAXN=100005; const long long mod=1e9+7; char s[MAXN]; int n; long long sum,cnt,ans; int main() { scanf("%d",&n); scanf("%s",s+1); for(int i=1;i<=n;++i) { sum=(sum+cnt)%mod; if(s[i]=='1') { ans=(ans+sum)%mod; ++cnt; } } printf("%lld\n",ans); return 0; } 第二种,静态维护区间和 https://blog.nowcoder.net/n/1c3504ca9e3f40d2a37376cd1b76ddec 然后我们发现我们这道题差不多,每个点上的1对后面的点的贡献是1 2 3 4 5 6 7 8 9.....然后我们ans就是遇到一个点为1 就加上这个点上的贡献。 那和我们上面那个链接里的有什么关系呢,你会发现每个点的贡献就是一个等差序列,这就符合上面那个了 然后我们直接改改 #include using namespace std; const int MAXN=100005; const long long mod=1e9+7; char s[MAXN]; int n; long long ans,sum[MAXN]; void pre_sum() { for(int i=1;s[i];++i) { sum[i]+=sum[i-1]; if(sum[i]>=mod)sum[i]-=mod; } } int main() { scanf("%d",&n); scanf("%s",s); for(int i=0;s[i];++i) { if(s[i]=='1') { sum[i+1]++; } } pre_sum(); pre_sum(); for(int i=1;s[i];++i) { if(s[i]=='1') { ans+=sum[i]; if(ans>=mod)ans-=mod; } } printf("%lld\n",ans); return 0; }
动态修改区间和(线段树走起
#include#include #include ///线段树,维护区间内1的数量cnt、区间的Link值w、区间内所有的 ///1到区间左边界的距离之和dl、区间内所有的1到区间右边界的距离 ///之和dr,查询时只要输出tree[1].w即可,修改时只用改变区间内 ///1的cnt,然后进行区间合并,区间合并的公式画个图推导一下即可 using namespace std; typedef long long ll; const int N = 100010; const ll mod = 1000000007; struct node { int l, r; ll dl, dr, w, cnt; }; node tree[4 * N]; char s[N]; int n, q; void update(int k) { tree[k].cnt = tree[2 * k].cnt + tree[2 * k + 1].cnt; ll t = tree[2 * k].cnt * tree[2 * k + 1].dl + tree[2 * k + 1].cnt * tree[2 * k].dr; tree[k].w = tree[2 * k].w + tree[2 * k + 1].w + t + tree[2 * k].cnt * tree[2 * k + 1].cnt; tree[k].dl = tree[2 * k].dl + tree[2 * k + 1].dl + tree[2 * k + 1].cnt * (tree[2 * k].r - tree[2 * k].l + 1); tree[k].dr = tree[2 * k].dr + tree[2 * k + 1].dr + tree[2 * k].cnt * (tree[2 * k + 1].r - tree[2 * k + 1].l + 1); } void build(int k, int lef, int rig) { tree[k].l = lef, tree[k].r = rig; if (tree[k].l == tree[k].r) { tree[k].w = tree[k].dl = tree[k].dr = 0; if ('1' == s[tree[k].l]) tree[k].cnt = 1; else tree[k].cnt = 0; return; } int mid = (lef + rig) / 2; build(k * 2, lef, mid); build(k * 2 + 1, mid + 1, rig); update(k); } void change_point(int k, int x, int y) { if (tree[k].l == tree[k].r) { tree[k].cnt = y; return; } int mid = (tree[k].l + tree[k].r) / 2; if (x <= mid) change_point(2 * k, x, y); else change_point(2 * k + 1, x, y); update(k); } int main() { scanf("%d%s", &n, s + 1); build(1, 1, n); printf("%lld\n", tree[1].w % mod); scanf("%d", &q); while(q--){ int kd, pos; scanf("%d%d", &kd, &pos); change_point(1, pos, 1 == kd ? 1 : 0); printf("%lld\n", tree[1].w % mod); } return 0; }
求在1~n内,询问含有恰好k给合数因子的数有几个
埃式筛筛出质数,然后对于合数再筛一遍,然后统计每个数字被筛到的次数。桶排序一下即可。 #includeusing namespace std; const int MAXN=100005; int f[MAXN],cnt[MAXN],n,m,x; bool prim[MAXN]; void init(int n) { for(int i=2;i<=n;++i) { prim[i]=true; } for(int i=2;i<=n;++i) { if(prim[i]) { for(int j=i+i;j<=n;j+=i) { prim[j]=false; } } else { for(int j=i;j<=n;j+=i) { cnt[j]++; } } f[cnt[i]]++; } } int main() { scanf("%d %d",&n,&m); init(n); while(m--) { scanf("%d",&x); printf("%d\n",f[x]); } }
题目大意入上图所述
具体思路实现:
首先,考虑问题的解法,因为任意时刻都不相同(没有同一个时刻刷出两个怪及以上的可能),那么令:
dp[i]代表到第i个时间点能获得的最大权值dp[i]代表到第i个时间点能获得的最大权值
那么这个题的状态转移方程其实也很好推的:
首先按照时间排序, 然后去找一下之前的时间点,能不能与这个时间点相连接,也就是说,假设前面有两个时间点
1 2 ,我考虑由1转移过来,还是由2转移过来,那么转移过来的条件就是,那个时间点的位置+那个时间点的时间到当前点的时间(k)<=当前时间点的时间(那就可以赶过来得到该权值)。
这时候 那个时间点的时间到当前点的时间 我们考虑最优状态转移,那么一定是k->i的最短路,所以我们要预处理一遍最短路。
所以m^2的算法也就显而易见,这时候去考虑如何优化状态,看一下 那个时间点的位置+那个时间点的时间到当前点的时间(k) 这个式子用符号化语言来表述的话就是:dis[i][k]+save[k].t<=save[i].tdis[i][k]+save[k].t<=save[i].t
这里其实dis[i][k]与dis[k][i]是相等的,因为图无向
考虑dis[i][k]的最大值 -> n ,因为全图只有n个点而且图联通。再基于没有时间点重复,也就说 i与i-n之前的所有的时间点 至少相差n,所以说i-n之前的可以不用去比较上述判断条件,直接转移,所以我们新开一个数组保留前缀最小,直接转移就可以了 ,复杂度 O(M*N) ~2e7
(意思就是我们每次dp【i】都是从1到i-1时间dp过去的,当这个i越来越大,那这样肯定超时,我们发现整个图就n个点,所以我们只需对于每个i枚举(i-n)~(i)就行,那1~1-n的呢,因为他们和i的差大于n,所以是不用进行上面的dp里的比较,
因为dp的比较是能不能够时间到达这个i点,但是我们差大于n的,所以我们是不用担心够不够时间的。
这其中有个坑,因为初始点是1,所以说起初的状态有可能从1过不来,所以dp数组的初始化不可以设置为-1,当然你可以令开一个bool数组标记当前时间点能否到达,能到达才进行转移,如果不开数组标记的话,将时间点设为-1e18(最小值),这样的话,只要不能到达的再怎么加也不可能使得状态转移。最后遍历得最大值得时候,将ans初始化为0,就可以略去负数情况(负数即为不可到达).
#include#include #include #include #pragma GCC optimize(2) using namespace std; typedef long long ll; const ll INF=1000000000000007; const ll maxn=1e6+5; const int mod=1e9+7; ll n,m,p; ll mp[205][205]; struct node{ ll p,w,t; bool operator<(const node&x)const{ return t n) dp[i]=max(dp[i],cop[i-n]+save[i].w); for(int k=1;k<=n&&i-k>=0;k++){ if(save[i-k].t+mp[save[i-k].p][save[i].p]<=save[i].t) dp[i]=max(dp[i],dp[i-k]+save[i].w); } ans=max(dp[i],ans); cop[i]=max(cop[i-1],dp[i]); // printf("%lld ",dp[i]); } printf("%lld\n",ans); return 0; }