呵呵
五一不做比赛,就总结比赛了。
做完了。。。
其实就C题和I题比较有价值。。。。
那就讲讲这两个题吧
C:
给你N个数,N个区间,要求判断是否存在一个唯一的匹配。
思路: 先随便求出一个匹配 , 然后判断这个匹配是否唯一。。。
求出一个匹配的话,可以先把区间按右端点排序,维护一个set,表示当前还没有匹配的点,每次在点集中找最小的但是大于等于当前区间左端点的点跟这段区间匹配。
算是贪心吧,如果有解,肯定能找到一个解。
接下来是判断解的唯一性,解不唯一的情况如下图所示
Pnow是当前点 Snow是当前点匹配的区间,只要当这段区间的左端点到Pnow之间的所有点中存在一个点Pi,Pi所对应的区间的右端点超过了Pnow,我们就可以互换这两个点的匹配。
#include<set> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int maxn = 100010; struct SegTree { int mx[maxn<<2]; void build(int l,int r,int rt) { mx[rt] = 0; if(l==r) return ; int m = l+r>>1; build(lson); build(rson); } void insert(int p,int val,int l,int r,int rt) { if(val>mx[rt]) mx[rt] = val; if(l == r) return ; int m = l + r >> 1; if(p <= m) insert(p,val,lson); else insert(p,val,rson); } int query(int L,int R,int l,int r,int rt) { if(L > R) return 0; if(L <= l && r <= R) { return mx[rt]; } int m = l + r >> 1; int ans = 0; if(L <= m) ans = max(query(L,R,lson),ans); if(R > m) ans = max(query(L,R,rson),ans); return ans; } }S; struct VAL{ int num; int id; bool operator < (const VAL&cmp) const{ return num < cmp.num; } }val[maxn]; struct SEG{ int l,r,id; bool operator < (const SEG&cmp) const { if(r != cmp.r) return r < cmp.r; return l < cmp.l; } }seg[maxn]; int mp[maxn],num[maxn]; int ans[maxn]; multiset<pair<int,int> > st; int main() { int n ; scanf("%d",&n); for(int i = 1; i <= n; i++) { scanf("%d",&val[i].num); val[i].id = i; num[i] = val[i].num; st.insert(make_pair(num[i],i)); } for(int i = 1; i <= n; i++) { scanf("%d%d",&seg[i].l,&seg[i].r); seg[i].id = i; } sort(num+1,num+n+1); sort(val+1,val+1+n); sort(seg+1,seg+1+n); S.build(1,n,1); for(int i = 1; i <= n; i++) { multiset<pair<int,int> >::iterator it = st.lower_bound(make_pair(seg[i].l,0)); if(it==st.end() || it->first > seg[i].r ) { puts("Let's search for another office."); return 0; } mp[it->second] = i; ans[seg[i].id] = it->second; st.erase(it); } for(int i = 1; i <= n; i++) { S.insert(i,seg[mp[val[i].id]].r,1,n,1); } bool flag = false; for(int i = 1; i <= n; i++) { int ss = mp[val[i].id]; int left = seg[ss].l; int idx = lower_bound(num+1,num+n+1,left) - num; int mx = S.query(idx,i-1,1,n,1); if(mx >= val[i].num) { flag = true; break; } } if(flag) { puts("Ask Shiftman for help."); } else { puts("Perfect!"); for(int i = 1; i < n; i++) printf("%d ",ans[i]); printf("%d\n",ans[n]); } return 0; }
I题:
构造题:题意略去。。
首先要YY出我们应该从最后一行开始构造
仔细观察可以发现除了第一行外,每行都是同号的,那么我们可以根据上面一行的增减性,确定当前行的符号,确定了符号后我们只需要确定最后一个数是什么就好了。
最后一个数确定了,前面所有的数都确定了。确定最后一个数的时候我们肯定是用最优的方法来选取的。比如,当前行是正数,而且当前行是递增的,那么第一个数就是1,组后一个数就是下面一行所有数的和+1,
第一行的数是例外的,如果递增,我们直接令最后一个数为10^9,递减的话,令最后一个数为-10^9
中间的运算一旦超出了题目要求的范围,直接输出impossible
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef __int64 lld; int flag[2010] , n; lld ans[2010]; bool solve() { int i , j ; reverse(flag+1,flag+1+n); ans[1] = flag[2] ? 1 : -1; for(i = 3; i <= n; i++) { lld sum = 0; for(j = 1; j <= i - 2; j++) sum = sum + ans[j]; if(flag[i]) { if(flag[i-1]) ans[i-1] = sum + 1; else ans[i-1] = 1; } else { if(flag[i-1]) ans[i-1] = -1; else ans[i-1] = sum - 1; } for(j = i - 2; j >= 1; j--) { ans[j] = ans[j+1] - ans[j]; if(ans[j] > (int)1e9 || ans[j] < (int)-1e9) return false; } } ans[n] = (flag[n]?1:-1)*(int)1e9; for(j = n - 1; j >= 1; j--) { ans[j] = ans[j+1] - ans[j]; if(ans[j] > (int)1e9 || ans[j] < (int)-1e9) return false; } for(i = 1; i <= n; i++) printf("%I64d ",ans[i]); return true; } int main() { scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&flag[i]); if(!solve()) puts("IMPOSSIBLE"); return 0; }