C、D、E
C:
每段类似X00000000...的数(X表示非零数字)如果X左边整段数字连起来都没有这段数字大的话,那么左边整段+当前这段就必须作为一个数字,要不然左边整段不可能在当前这段前面,程序到这里就可以结束了。否则,左边整段可以继续分解。对于非零的一整段,可以第一个数连第二个数字一直连起来,答案是这段的长度。除非这段在最开头,且第一个数字小于第二个数字,那么前两个数字必须作为一个数,答案是长度-1。
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))
#define mp make_pair
#define fir first
#define sec second
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
/*----------------code-----------------*/
char s[N];
int solve(int begin,int end){
if(end<begin) return 0;
if(begin==0){
if(end==begin) return 1;
if(s[begin]>=s[begin+1]) return end-begin+1;
return end-begin;
}
return end-begin+1;
}
bool check(int begin,int end,int _begin,int _end){
if(end<0) return true;
if(end-begin<_end-_begin) return false;
if(end-begin>_end-_begin) return true;
if(s[begin]>=s[_begin]) return true;
return false;
}
int main(){
scanf("%s",s);
int n=strlen(s),end=n-1,ans=0;
for(int i=n-1;i>=0;i--){
if(s[i]=='0'){
ans+=solve(i+1,end);
int t=i;
while(i>=0 && s[i]=='0') i--;
if(!check(0,i-1,i,t)){
ans++;
end=-1;
break;
}else{
ans++;
}
end=i-1;
}
}
ans+=solve(0,end);
printf("%d",ans);
return 0;
}
D:
看错题意,一直把arc (u,v)理解成存在路径u到v,虽然我准确翻译了“arc”的意思,但是当时就是糊涂。。。然后就不会做了。这里的意思就是存在有向边u->v。
枚举中心点v。
1.每个点都必须有一条指向v的边和从v指向该点的边,统计点v的出入度下就知道这里缺几条边了。
2.因为每个点需要出度2,入度2,再去掉和v连着的两条边,那么只需要出度1,入度1。想象一下,把一个点拆成两个,一个“出点”,一个“入点”。只有从“出点”有 有向边指向“入点”。那么这就是个“二分图”,而我们现在只需要出度1,入度1,所以一个“出点”匹配一个“入点”。做一下二分图最大匹配,就知道最多能保留几条边,多余的边删掉。实际操作的时候不需要真的拆点。
3.删完多余边之后,还有一些点不满足出度1,入度1,还需要再补边。
上面3个步骤需要的操作次数,就是点v为中心点的时候的最小操作次数。
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 512
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
#define mp make_pair
#define fir first
#define sec second
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
/*----------------code-----------------*/
int n,m,in[N],out[N],match[N];
bool g[N][N],vis[N];
bool augment(int v,int u){
for(int i=1;i<=n;i++){
if(i==v || !g[u][i] || vis[i]) continue;
vis[i]=true;
if(match[i]==-1 || augment(v,match[i])){
match[i]=u;
return true;
}
}
return false;
}
int hungary(int v){
int ans=0;
CLR(match,-1);
for(int i=1;i<=n;i++) if(v!=i){
CLR(vis,0);
if(augment(v,i)) ans++;
}
return ans;
}
int solve(int v){
int vEdges=in[v]+out[v]-g[v][v];
int ans=2*n-1-vEdges;
int Max=hungary(v);
ans+=m-vEdges-Max;
ans+=n-1-Max;
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int x,y;
scanf("%d%d",&x,&y);
g[x][y]=true;
in[y]++, out[x]++;
}
int ans=INF;
for(int v=1;v<=n;v++)
ans=min(ans,solve(v));
printf("%d\n",ans);
return 0;
}
E:
题目的意思其实就是每次会移除你选的这段连续区间里最小的数字,而且给的n个数是不重复的。
如果区间[l,r]内最小的数不在b[]里,显然w=r-l+1,然后移除掉这个最小数。
如果在b[]里,设它的位置是pos,那么我们只能去区间[l,pos-1]和[pos+1,r]里继续寻找答案了。
用线段树维护区间最小值,最小值的位置,以及移除操作和统计区间还剩多少个数。
最多n-k次移除操作,复杂度O((n-k)log(n))。
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 1000100
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
#define mp make_pair
#define fir first
#define sec second
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
/*----------------code-----------------*/
int n,m;
bool vis[N];
class segTree{
#define lson (rt<<1)
#define rson (rt<<1|1)
private:
struct segment{
int l,r,sum,min;
}seg[N<<2];
public:
void pushup(int rt){
seg[rt].min=min(seg[lson].min,seg[rson].min);
seg[rt].sum=seg[lson].sum+seg[rson].sum;
}
void build(int l,int r,int rt){
seg[rt].l=l, seg[rt].r=r;
if(l==r){
scanf("%d",&seg[rt].min);
seg[rt].sum=1;
return ;
}
int mid=(seg[rt].l+seg[rt].r)>>1;
build(l,mid,lson);
build(mid+1,r,rson);
pushup(rt);
}
int remove(int val,int L,int R,int rt){
if(seg[rt].l==seg[rt].r){
if(seg[rt].min!=val) return 0;
seg[rt].min=INF;
seg[rt].sum=0;
return seg[rt].l;
}
int pos=0;
if(L<=seg[rt].l && seg[rt].r<=R){
if(seg[lson].min==val) pos=remove(val,L,R,lson);
else if(seg[rson].min==val) pos=remove(val,L,R,rson);
}else{
int mid=(seg[rt].l+seg[rt].r)>>1;
if(L<=mid) pos+=remove(val,L,R,lson);
if(mid<R) pos+=remove(val,L,R,rson);
}
pushup(rt);
return pos;
}
int getSum(int L,int R,int rt){
if(L<=seg[rt].l && seg[rt].r<=R) return seg[rt].sum;
int mid=(seg[rt].l+seg[rt].r)>>1,sum=0;
if(L<=mid) sum+=getSum(L,R,lson);
if(mid<R) sum+=getSum(L,R,rson);
return sum;
}
int getMin(int L,int R,int rt){
if(L<=seg[rt].l && seg[rt].r<=R) return seg[rt].min;
int mid=(seg[rt].l+seg[rt].r)>>1,Min=INF;
if(L<=mid) Min=min(Min,getMin(L,R,lson));
if(mid<R) Min=min(Min,getMin(L,R,rson));
return Min;
}
}T;
ll solve(int l,int r){
if(r<l) return 0;
ll ans=0;
int val,len=T.getSum(l,r,1);
if(len==0) return 0;
while( len && !vis[(val=T.getMin(l,r,1))] ){
ans+=len--;
T.remove(val,l,r,1);
}
int pos=T.remove(val,l,r,1);
return ans+solve(l,pos-1)+solve(pos+1,r);
}
int main(){
scanf("%d%d",&n,&m);
T.build(1,n,1);
for(int i=0;i<m;i++){
int x;
scanf("%d",&x);
vis[x]=true;
}
printf("%I64d\n",solve(1,n));
return 0;
}