Rank:56/147.
开场看B,是个线段树区间合并,花了2hour敲完代码。。。再花了30min查错。。发现push_down有问题。改了就AC了。
然后发现A过了很多人。推了个公式,发现是个分段函数。取个中间点代进去结果就AC了。
C好像也很水?看了看原来是个傻逼BFS。A了。
F化解了一下就 集合子集异或为0的种数。想了想没想到什么东西就go die了。
A.Liang Guo Sha(阅读理解)
看懂题意后搞出两个式子,然后取个极值就行了。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # 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 # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... int main () { int a, b, c; while (~scanf("%d%d%d",&a,&b,&c)){ double p=((b+c)*1.0)/(a+b+2*c); double ans=a*p*p+b*(1-p)*(1-p)-c*(1-p)*p*2; printf("%.6f ",ans); } return 0; }
B.Black And White(线段树)
询问区间[l,r]的最长连续黑色的长度。
建立线段树,对每个节点维护6个量,分别表示最长连续黑色,最长连续白色,左边最长连续黑色,左边最长连续白色,右边最长连续黑色,右边最长连续白色。
对于修改操作用tag标记一下。在需要push_down的时候push_down即可AC。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # 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 # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... struct SegTree{int lbmax, lwmax, rbmax, rwmax, bmax, wmax;}seg[N<<2]; struct Node{ int lmax, rmax, ma, siz; Node(int _lmax=0, int _rmax=0, int _ma=0, int _siz=0):lmax(_lmax), rmax(_rmax), ma(_ma), siz(_siz){} }; int a[N]; bool tag[N<<2]; void push_up(int p, int l, int r){ int lsize=(l+r)/2-l+1, rsize=r-(l+r)/2; if (seg[p<<1].lbmax) { seg[p].lbmax=(seg[p<<1].lbmax==lsize?seg[p<<1|1].lbmax:0)+seg[p<<1].lbmax; seg[p].lwmax=0; } else { seg[p].lwmax=(seg[p<<1].lwmax==lsize?seg[p<<1|1].lwmax:0)+seg[p<<1].lwmax; seg[p].lbmax=0; } if (seg[p<<1|1].rbmax) { seg[p].rbmax=(seg[p<<1|1].rbmax==rsize?seg[p<<1].rbmax:0)+seg[p<<1|1].rbmax; seg[p].rwmax=0; } else { seg[p].rwmax=(seg[p<<1|1].rwmax==rsize?seg[p<<1].rwmax:0)+seg[p<<1|1].rwmax; seg[p].rbmax=0; } seg[p].bmax=max(max(seg[p<<1].bmax,seg[p<<1|1].bmax),seg[p<<1].rbmax+seg[p<<1|1].lbmax); seg[p].wmax=max(max(seg[p<<1].wmax,seg[p<<1|1].wmax),seg[p<<1].rwmax+seg[p<<1|1].lwmax); } void push_down(int p){ if (!tag[p]) return ; tag[p<<1]^=1; tag[p<<1|1]^=1; tag[p]=0; swap(seg[p].bmax,seg[p].wmax); swap(seg[p].lbmax,seg[p].lwmax); swap(seg[p].rbmax,seg[p].rwmax); } void init(int p, int l, int r){ if (l<r) { int mid=(l+r)>>1; init(lch); init(rch); push_up(p,l,r); tag[p]=0; } else { if (a[l]) seg[p].lbmax=seg[p].rbmax=seg[p].bmax=1, seg[p].lwmax=seg[p].rwmax=seg[p].wmax=0; else seg[p].lwmax=seg[p].rwmax=seg[p].wmax=1, seg[p].lbmax=seg[p].rbmax=seg[p].bmax=0; tag[p]=0; } } void update(int p, int l, int r, int L, int R){ push_down(p); if (L>r||R<l) return ; if (L<=l&&R>=r) tag[p]=1, push_down(p); else { int mid=(l+r)>>1; update(lch,L,R); update(rch,L,R); push_up(p,l,r); } } Node query(int p, int l, int r, int L, int R){ push_down(p); if (L>r||R<l) return Node(0,0,0,0); if (L<=l&&R>=r) return Node(seg[p].lbmax,seg[p].rbmax,seg[p].bmax,r-l+1); int mid=(l+r)>>1; Node x=query(lch,L,R), y=query(rch,L,R), z; z.lmax=(x.lmax==x.siz?y.lmax:0)+x.lmax; z.rmax=(y.rmax==y.siz?x.rmax:0)+y.rmax; z.ma=max(max(x.ma,y.ma),x.rmax+y.lmax); z.siz=x.siz+y.siz; return z; } int main () { int n, m, flag, l, r; while (~scanf("%d",&n)) { FOR(i,1,n) scanf("%d",a+i); init(1,1,n); scanf("%d",&m); FOR(i,1,m) { scanf("%d%d%d",&flag,&l,&r); if (flag) update(1,1,n,l,r); else { printf("%d ",query(1,1,n,l,r).ma); } } } return 0; }
C.Turn Right(BFS)
题目保证一定存在起点到终点的路径。如果把终点给封死,那么从起点bfs一次再回到起点就行了。沿途统计所有标记。
这题的节点状态与普通的不同,需要定义vis[i][j]表示到点i是以j方向到的。然后对于每个方向会有一个优先级。即总是turn right。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # 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 # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=505; //Code begin... int vis[N][N][4], row[N][N], col[N][N]; int ps[4][4]={{3,0,1,2},{0,1,2,3},{1,2,3,0},{2,3,0,1}}; struct Node{ int x, y, pos; Node(int _x=0, int _y=0, int _pos=0):x(_x),y(_y),pos(_pos){} }; queue<Node>Q; int main () { int T, n, m, s, t; scanf("%d",&T); while (T--) { int num=0; mem(vis,0); scanf("%d%d%d%d",&n,&m,&s,&t); FOR(i,1,2*n-1) { if (i&1) FO(j,0,m-1) scanf("%d",&col[(i-1)/2][j]); else FO(j,0,m) scanf("%d",&row[i/2][j]); } while (!Q.empty()) Q.pop(); Q.push(Node(0,s,1)); vis[0][s][1]=1; while (!Q.empty()) { Node tmp=Q.front(); Q.pop(); FO(i,0,4) { int now=ps[tmp.pos][i]; if (now==0) { if (tmp.y==0||col[tmp.x][tmp.y-1]) continue; if (vis[tmp.x][tmp.y-1][now]) goto label; Q.push(Node(tmp.x,tmp.y-1,now)); vis[tmp.x][tmp.y-1][now]=1; break; } else if (now==1) { if (tmp.x==n-1||row[tmp.x+1][tmp.y]) continue; if (vis[tmp.x+1][tmp.y][now]) goto label; Q.push(Node(tmp.x+1,tmp.y,now)); vis[tmp.x+1][tmp.y][now]=1; break; } else if (now==2) { if (tmp.y==m-1||col[tmp.x][tmp.y]) continue; if (vis[tmp.x][tmp.y+1][now]) goto label; Q.push(Node(tmp.x,tmp.y+1,now)); vis[tmp.x][tmp.y+1][now]=1; break; } else { if (tmp.x==0||row[tmp.x][tmp.y]) continue; if (vis[tmp.x-1][tmp.y][now]) goto label; Q.push(Node(tmp.x-1,tmp.y,now)); vis[tmp.x-1][tmp.y][now]=1; break; } } } label: FO(i,0,n) FO(j,0,m) FO(k,0,4) if (vis[i][j][k]) {++num; break;} puts(num==n*m?"YES":"NO"); } return 0; }
F.Game(高斯消元)
要想去除某些石堆使得先手必败。那么s[i]^s[i+1]^...s[n]=0.即问题转化为求出集合S的子集异或和为0的子集种数。
集合S里的每个数,要么选要么不选,考虑每一位的贡献,那么选了这个数,这个数上二进制位为1的位会贡献1.
于是对于每位,建立异或方程组,再用高斯消元求解这个方程的自由变元的数量即可。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000007 # 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 # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=105; //Code begin... int equ, var, a[35][N]; LL pow_mod(LL a, LL n, LL mod){ LL ret=1, temp=a%mod; while (n) { if (n&1) ret=ret*temp%mod; temp=temp*temp%mod; n>>=1; } return ret; } int Guass(){ int row, col; for(row=0,col=0; row<equ&&col<var; ++col,++row){ int max_r=row; FO(i,row+1,equ) { if(a[row][col]==1) break; if(a[max_r][col]<a[i][col]){max_r=i; break;} } if(max_r!=row) FOR(j,0,var) swap(a[max_r][j],a[row][j]); if(a[row][col]==0) row--; FO(i,row+1,equ) { if(a[i][col]==0) continue; FOR(j,col,var) a[i][j]^=a[row][j]; } } FO(i,row,equ) if(a[i][col]) return -1; return var-row; } int main () { int T, n, val; scanf("%d",&T); while (T--) { mem(a,0); scanf("%d",&n); equ=31; var=n; FO(i,0,n) { scanf("%d",&val); FO(j,0,equ) a[j][i]=val%2, val/=2; } FO(j,0,equ) a[j][n]=0; int b=Guass(); printf("%lld ",pow_mod(2,b,MOD)); } return 0; }
G.Sequence Decomposition(贪心+树状数组)
给出一个序列,每次可以将连续的一段都减去1,每次操作的长度定义为连续段的长度,问最后使得序列都变为0时,最短的操作长度最长是多少。
首先,将操作改变顺序是不影响答案的。不妨从左端点1开始,依次向后看右端点的最佳选值.
如果有a[i]<=a[i+1],那么右端点就可以从i拓展到i+1了。如果有a[i]>a[i+1].那么右端点就不能再继续拓展了。反证证明这个贪心的正确性。
但是如果直接模拟操作的话显然是超时的。注意到每次的操作实际上是区间减,查询单点。可以用树状数组维护。
复杂度O(nlogn).
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000007 # 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 # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=10005; //Code begin... int a[N], tree[N], n; void add(int x, int val){while (x<=n+1) tree[x]+=val, x+=lowbit(x);} int query(int x){ int res=0; while (x) res+=tree[x], x-=lowbit(x); return res; } int main () { int T, ans; scanf("%d",&T); while (T--) { mem(tree,0); ans=INF; scanf("%d",&n); FOR(i,1,n) scanf("%d",a+i), add(i,a[i]), add(i+1,-a[i]); a[n+1]=0; int now=1; FOR(i,1,n) { if (a[i]<=a[i+1]) continue; int tmp=a[i]-a[i+1]; add(now,-tmp); add(i+1,tmp); FOR(j,now,i) if (query(j)>=0) {ans=min(i-j+1,ans); now=j; break;} } printf("%d ",ans); } return 0; }
H.Road constructions(最大权闭合子图)
裸题。。。不明白考试为啥不看。。。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000007 # 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 # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=5005; //Code begin... struct Edge{int p, next, w;}edge[500005]; struct Node{int pre, suc, w, id;}node[3005]; int head[N], cnt=2, s, t, vis[N]; int val[N]; queue<int>Q; void add_edge(int u, int v, int w){ edge[cnt].p=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].p=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt++; } int bfs(){ int i, v; mem(vis,-1); vis[s]=0; Q.push(s); while (!Q.empty()) { v=Q.front(); Q.pop(); for (i=head[v]; i; i=edge[i].next) { if (edge[i].w>0 && vis[edge[i].p]==-1) { vis[edge[i].p]=vis[v] + 1; Q.push(edge[i].p); } } } return vis[t]!=-1; } int dfs(int x, int low){ int i, a, temp=low; if (x==t) return low; for (i=head[x]; i; i=edge[i].next) { if (edge[i].w>0 && vis[edge[i].p]==vis[x]+1){ a=dfs(edge[i].p,min(edge[i].w,temp)); temp-=a; edge[i].w-=a; edge[i^1].w += a; if (temp==0) break; } } if (temp==low) vis[x]=-1; return low-temp; } void init(){mem(head,0); mem(val,0); cnt=2;} int main () { int n, m, k; while (scanf("%d%d",&n,&m), n+m) { int res=0, sum=0, tmp; s=0; t=m+1; init(); FOR(i,1,m) scanf("%d",&tmp), res+=tmp, add_edge(s,i,tmp); scanf("%d",&k); FOR(i,1,k) scanf("%d%d%d%d",&node[i].pre,&node[i].suc,&node[i].id,&node[i].w), val[node[i].id]+=node[i].w; FOR(i,1,k) FOR(j,i+1,k) { if (node[i].id==node[j].id) continue; if (node[i].suc==node[j].pre) add_edge(node[i].id,node[j].id,INF); else if (node[i].pre==node[j].suc) add_edge(node[j].id,node[i].id,INF); } FOR(i,1,m) add_edge(i,t,val[i]); while (bfs()) while (tmp=dfs(s,INF)) sum+=tmp; printf("%d ",res-sum); } return 0; }