A:
先处理type1,然后type2
B:
看这个数据范围直接暴力,一个一个放进去就可以了,也可以尽可能平摊这个区间,剩余的k个分给前k个就行了,反正有解的话肯定是可行的。
C:
简单的遍历一棵树,如果某个节点下面没有坏边并且父边是坏边,则加入答案中。
#include
#include
#include
using namespace std;
const int maxn = 100000 + 10;
struct Edge {
int to, next, k;
}edge[maxn<<1];
int head[maxn], E, pr[maxn];
void init() {
memset(head, -1, sizeof(head));
E = 0;
}
void newedge(int u, int to, int k) {
edge[E].to = to;
edge[E].k = k;
edge[E].next = head[u];
head[u] = E++;
}
bool dfs(int u, int pre, bool flag) {
bool yes = false;
for(int i = head[u];i != -1;i = edge[i].next) {
int to = edge[i].to;
if(to == pre) continue;
if(dfs(to, u, edge[i].k == 2)) yes = true;
}
if(!yes && flag) pr[u] = 1;
return yes || flag;
}
int main() {
init();
int n, u, to, k;
scanf("%d", &n);
for(int i = 0;i < n-1; i++) {
scanf("%d%d%d", &u, &to, &k);
newedge(u, to, k);
newedge(to, u, k);
}
dfs(1, -1, 0);
int ans = 0;
for(int i = 1;i <= n; i++) ans += pr[i];
printf("%d\n", ans);
for(int i = 1;i <= n; i++) if(pr[i]) printf("%d ", i); puts("");
return 0;
}
D:
思路:可以知道所有的状态一定可以用 (i, j)表示,i前面的肯定全被杀死了,i 、j中间的也都被杀死了,j后面的都还活着,所以总的情况数是 n^2的。这样子的话我用t[i]表示剩余的人为 i ~ n至少需要几秒,对于每个i都判断下j (j > i)这个位置是否可行,表示状态 (i, j),有种特殊情况是最后全部都死了,这个的话判断什么时候只剩下两个人的时候他们的p都是大于0的就可行,用个flag标记下即可。
#include
#include
#include
using namespace std;
const int maxn = 3000 + 10;
const int INF = 1<<30;
int p[maxn], f1[maxn], f2[maxn], t[maxn];
int main() {
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1;i <= n; i++)
scanf("%d", &p[i]);
for(int i = 1;i <= n; i++) t[i] = i == 1 ? 0 : INF;
for(int i = n;i >= 1; i--) {
if(f1[i+1] || p[i] == 100) f1[i] = 1;
if(f2[i+1] || p[i] > 0) f2[i] = 1;
}
int ans = 0;
bool ok = 0;
for(int i = 1;i <= n; i++) if(t[i] <= k) {
if(p[i] != 100 && f2[i+1]) {
t[i+1] = min(t[i+1], t[i]+1);
}
ans++;
if(p[i] > 0) {
if(f1[i+1]) {
t[i+2] = min(t[i+2], t[i] + 1);
if(i + 1 == n) ok = 1;
}
else {
ans += min(k - t[i], n - i);
for(int j = i+1;j <= n; j++) if(f2[j] && t[i] + j - i <= k){
if(j == n) ok = 1;
t[j+1] = min(t[j+1] , t[i] + j - i);
if(p[i] != 100) t[j] = min(t[j], t[i] + j - i);
}
}
}
else {
if(f2[i+1]) t[i+1] = min(t[i+1], t[i] + 1);
}
}
if(ok) ans++;
printf("%d\n", ans);
return 0;
}
E:
给你一些线段,然后每次询问一些点,问这些点在多少条不同线段中。
思路:
对于每次询问的这些点,相邻两个点建一个线段,在最后一个点后加一个超远点和它建一个线段,所有的询问都这样处理,然后按右端点从小到大排序。对于原始线段也按右端点从小到大排序,每次拿出新建的线段,将原始线段中右端点值小于该线段右端点的线段插入到线段树中,然后询问新建的线段的左端点就可以了,这样子不会出现重复的,画个图想想就明白了。
#include
#include
#include
using namespace std;
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
const int maxn = 1000000 + 100;
struct PP {
int l, r, id;
bool operator < (const PP &a) const {
return r < a.r;
}
}a[maxn], b[maxn];
int mark[maxn<<2], pr[maxn];
void down(int rt) {
if(mark[rt]) {
mark[rt<<1] += mark[rt];
mark[rt<<1|1] += mark[rt];
mark[rt] = 0;
}
}
void update(int rt, int l, int r, int L, int R) {
if(L <= l && r <= R) {
mark[rt] ++;
return ;
}
down(rt);
int mid = (l + r)/2;
if(L <= mid) update(lson, L, R);
if(R > mid) update(rson, L, R);
}
int query(int rt, int l, int r, int x) {
if(l == r && l == x) {
return mark[rt];
}
down(rt);
int mid = (l + r)/2;
if(x <= mid) return query(lson, x);
else return query(rson, x);
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0;i < n; i++)
scanf("%d%d", &a[i].l, &a[i].r);
int tot = 0, mx = 1000000+1;
for(int i = 0;i < m; i++) {
int cnt, x;
scanf("%d", &cnt);
scanf("%d", &x);
for(int j = 0;j < cnt-1; j++) {
scanf("%d", &b[tot].r);
b[tot].l = x; b[tot].id = i;
x = b[tot++].r;
}
b[tot].id = i; b[tot].l = x; b[tot++].r = mx;
}
sort(a, a + n);
sort(b, b + tot);
int cur = -1;
for(int i = 0;i < tot; i++) {
while(cur+1 < n && a[cur+1].r < b[i].r)
update(1, 1, mx, a[cur+1].l, a[cur+1].r) , cur++;
int pp = query(1, 1, mx, b[i].l);
pr[b[i].id] += pp;
}
for(int i = 0;i < m; i++) printf("%d\n", pr[i]);
return 0;
}