A:
从最左边向右的牛,和最右边向左的牛中取影响最小的先挤奶。一直比下去。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 200010
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD =1000000007;
const double EPS=1e-7;
int n;
int a[N],l[N],r[N];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
l[i]+=l[i-1]+(a[i]==0);
r[i]+=r[i-1]+(a[i]==1);
}
int L,R;
for(int i=1;i<=n;i++) if(a[i]==1){
L=i;
break;
}
for(int i=n;i>=1;i--) if(a[i]==0){
R=i;
break;
}
ll ans=0;
for(int i=L,j=R;i<j;){
while(i<=n && a[i]!=1) i++;
while(j>=1 && a[j]!=0) j--;
if(i>=j || i==n+1 || j==0) break;
ans+=min(l[j]-l[i],r[j]-r[i-1]);
if(l[j]-l[i]<r[j]-r[i-1]) i++;
else j--;
}
cout<<ans<<endl;
return 0;
}
B:
这题应该改成D题的。。。总感觉这场难度顺序放错了。。
这题的做法是这样的。
黄点是人在的位置,蓝点是不可经过的点。
对于(x,y)这个蓝点,如果在它右边的Y+1的地方,x-1及以下的位置有蓝点的话,那么这两个蓝点就能形成一条封锁线。
封锁线以下的区域是不可能走到的(即灰色段)。如果封锁线组成的分锁链能完全分隔(1,1)和(n,n)那么就无法到达,否则答案必然是2*n-2。
封锁线可以用有向边表示。
所以我们从左下的蓝点开始,从左往右连,从下往上连,到右上的蓝点。如果存在那么一条路径,能从左下的蓝点走到右上的蓝点,那么说明能分隔开。这里的左下指的是(x==n || y==1),右上(x==1 || y==n),必须是这样在左下边界的蓝点走到右上边界的蓝点才能完全分隔开(1,1)和(n,n)。
上面说的是以y坐标为基准的情况,x的也类似处理。
最后封锁链可能类似这样:
图画的丑了点。。。不喜勿喷。。。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 100010
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD =1000000007;
const double EPS=1e-7;
map<int,set<PI > > line[2];
vector<int> e[N];
bool vis[N];
void dfs(int u){
vis[u]=true;
for(int i=0;i<e[u].size();i++)
if(!vis[e[u][i]]) dfs(e[u][i]);
}
void solve(int op){
map<int,set<PI > >::iterator i;
set<PI >::iterator j,k;
for(i=line[op].begin();i!=line[op].end();i++){
int cur=i->first;
for(j=line[op][cur].begin();j!=line[op][cur].end();j++){
set<PI > &next=line[op][cur+1];
k=next.lower_bound((PI){j->first-1,0});
if(k!=next.end()){
if(op==0) // down to up
e[k->second].push_back(j->second);
else //left to right
e[j->second].push_back(k->second);
}
}
}
}
int main(){
int n,m,s=0,t;
scanf("%d%d",&n,&m);
t=m+1;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
line[0][x].insert((PI){y,i});
line[1][y].insert((PI){x,i});
if(x==n || y==1) e[t].push_back(i);
if(x==1 || y==n) e[i].push_back(s);
}
solve(0);
solve(1);
dfs(t);
if(vis[s]) puts("-1");
else printf("%d\n",2*n-2);
return 0;
}
C:
显然增加一个点的val,深度和它同奇偶的子树中的点也会+val,非同奇偶的则-val。
然后时间戳重标记树,维护奇偶两个树状数组,区间更新,点查询即可。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 200010
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD =1000000007;
const double EPS=1e-7;
class BIT{
public:
int n,sum[N];
void init(int n){
this->n=n;
fill(sum+1,sum+n+1,0);
}
void add(int i,int x){
for(;i>=1;i-= -i&i){
sum[i]+=x;
}
}
void update(int l,int r,int x){
if(r<l) return ;
add(l-1,-x);
add(r,x);
}
int query(int i){
int ans=0;
for(;i<=n;i+= -i&i){
ans+=sum[i];
}
return ans;
}
}T[2];
int val[N],l[N],r[N],h[N],Time;
vector<int> e[N];
void dfs(int u,int far,int step){
l[u]=++Time;
h[u]=step;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==far) continue;
dfs(v,u,step+1);
}
r[u]=Time;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",val+i);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,-1,0);
T[0].init(n);
T[1].init(n);
while(m--){
int op,x,c;
scanf("%d%d",&op,&x);
if(op==1){
scanf("%d",&c);
T[h[x]%2].update(l[x],r[x],c);
T[(h[x]%2)^1].update(l[x],r[x],-c);
}else{
int ans=T[h[x]%2].query(l[x])+val[x];
printf("%d\n",ans);
}
}
return 0;
}
D:
dp[i][val] 表示以第i个数结尾,构造出的和为val的方案数。
ans=sum{dp[i][0] }
每次转移后,还需要让dp[i][a]++和dp[i][-a]++,这相当于设置一个新的起点。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 1024
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD =1000000007;
const double EPS=1e-7;
int dp[N][20010];
int main(){
int n,a,ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a);
for(int j=0;j<=20000;j++) if(i-1>=0){
dp[i][j]+=dp[i-1][j-a];
dp[i][j]+=dp[i-1][j+a];
dp[i][j]%=MOD;
}
ans+=dp[i][10000];
ans%=MOD;
dp[i][10000+a]++;
dp[i][10000-a]++;
}
printf("%d\n",ans);
return 0;
}