A 只有判段相邻的差是不是有奇数,有奇数NO,反之YES
B 因为题目说随便两个相等+另一个数就是YES,所以map存第一次出现的数字的位置,如果再次出现,只要那个数的第一个位置不是i-1就是YES反之NO
C 蛋疼题意,其实只要找两个RR之间最短的距离+1就行了
D
题意:
老师的n个想法价值ai,同学的n个想法价值bi,相互对应,找一共有多少对 ai+aj-bi-bj>0
思路:
直接将老师的思路减学生的思路,从小到大排序,用upper_bound去找他的相反数的大一点的位置,复杂度是O(lgn*n)
但是n是2e5的范围,答案可能会爆int,别忘了开ll
#include <bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 const int maxn=2e5+10; int a[maxn],b; int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=0;i<n;i++) { scanf("%d",&b); a[i]-=b; } sort(a,a+n); ll ans=0; for(int i=0;i<n;i++){ int zhi=-a[i]; int pos=upper_bound(a+i+1,a+n,zhi)-a; ans+=(ll)n-pos; } printf("%lld ",ans); return 0; }
E
题意(题目描述进行魔改,但内容大致不变):
给n,h,l,r 四个值,表示n长度的数组,0~h-1的范围,在【l,r】区间得分能获得一个奖品(1<=n<=2000)
接下来的n个数,是按顺序的比赛得分,但是你有一个权力,每次得分可以保持不变,或者减一,然后进行累加,如果超出h,%h,问最多有多少奖品
思路:
dp【i】【j】,i表示第i个数字,j表示前面减了几次一;
cc数字i 全部累加情况(无减一)- j,这里有一个坑就是cc可能小于0,一定要while(cc<0)cc+h;而不是(cc+h)%h,因为这个h可能过小。//u1s1,比赛的时候如果写出,也会在这里被hack,因为惯性思维下意识写了(cc+h)%h
状态转移方程就是
k=(l<=cc && r>=cc);
dp[i][j]=max(dp[i-1][j-1]+k,dp[i-1][j]+k); (当j==0的时候特判一下,因为没有-1这种)
最后比较max(dp【n】【i】0<=i<=n)中
#include<iostream> #include<map> #include<set> #include<queue> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 const int maxn=2e3+10; int n,m,t,h,l,r; int a[maxn],dp[maxn][maxn]; int zhi[maxn][2]; int main(){ scanf("%d%d%d%d",&n,&h,&l,&r); int ans=0,c=0;mem(dp,0); for(it i=1;i<=n;i++){ scanf("%d",&a[i]); } for(it i=1;i<=n;i++){ c+=a[i];c%=h; if(l<=c && r>=c){ dp[i][0]=dp[i-1][0]+1; } else{ dp[i][0]=dp[i-1][0]; } for(it j=1;j<=i;j++){ int cc=(c-j+h)%h;while(cc<0){cc+=h;} if(l<=cc && r>=cc){ dp[i][j]=max(dp[i-1][j-1]+1,dp[i-1][j]+1); } else{ dp[i][j]=max(dp[i-1][j-1],dp[i-1][j]); } } } for(int i=0;i<=n;i++){ ans=max(dp[n][i],ans); } printf("%d ",ans); return 0; }
F
题意:
树上每个点要么黑色要么白色
假设每个点为根的话,求这个点的所有子树里面最大的白点-黑点
思路:
先确定1为根结点,dfs从叶子结点往上递归,叶子结点白为1,黑为-1,其他节点看子节点累加上来大于0的加,小于0的就不加了
如果把每个点看成根,其实就是把它的父节点当成子节点,原本第一次dfs把所有子节点弄个好了,父结点因为受到这个点的影响,可能变大,可能为0或者不变,所以从1这个根结点往下遍历,求这个点的子节点对自己的影响的其他部分,在子节点加上,便是答案。
#include<iostream> #include<map> #include<set> #include<queue> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 const int maxn=2e5+10; vector<int>g[maxn]; int a[maxn],n,dp[maxn],ans[maxn]; void dfs1(int root,int fa){ dp[root]=a[root]; for(auto &i:g[root]){ if(i==fa){continue;} dfs1(i,root); dp[root]+=max(0,dp[i]); } } void dfs2(int root,int fa,int zhi){ ans[root]=dp[root]+zhi; for(auto &i:g[root]){ if(i==fa){continue;} dfs2(i,root,max(ans[root]-max(dp[i],0),0)); } } int main(){ scanf("%d",&n); for(it i=1;i<=n;i++){ scanf("%d",&a[i]);if(!a[i]){a[i]=-1;} } for(it i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } dfs1(1,0); dfs2(1,0,0); for(it i=1;i<=n;i++){ printf(i==n?"%d ":"%d ",ans[i]); } return 0; }