3.10 jzoj 3967. Counting Friends

1 篇文章 0 订阅
1 篇文章 0 订阅

Description

FJ 的N 头奶牛(2<= N<= 500)都加入了社交网络“哞不可” 。
每头奶牛有一个或多个与它们自己在哞不可上互相关注的朋友。为了好玩,FJ 制作了一个列表,记下每头奶牛的朋友数目。但是,在书写列表的过程中,农夫John 惆怅了,以至于他错误地写下了一个额外的数字(因此他的列表包含N + 1 个数字,非他预计的N 个数字)。
请帮助FJ 找出在他的列表中有哪些数字可以是那个错误的额外数字。

Input

第一行:整数N。
第2 至N + 2 行:第i + 1 行包含FJ 的某一头奶牛的朋友数量,或者也许是那个错误的额外数字。

Output

第一行:一个整数K 给出在FJ 的列表中有多少项目可能是那个额外的数字(或者,K = 0 意味着没有一个数字,移除后可以产生一个可行的朋友配对)。
第2 至K + 1 行:每行包含可能是额外的数字按照输入顺序产生的序号(1到N +1)——也就是说,一个移除后,剩下的N 个数字符合奶牛们某一个可行
的朋友关系集合。这K 行的序号须按从小到大排列。

Sample Input

4
1
2
2
1
3

Sample Output

3
1
4
5
样例解释:
FJ 有4 头奶牛。两头每头只有一个朋友,两头各有两个朋友,还有一头有三个朋友(当然,这些数字其中一个是额外的并且不属于这个列表)。
移除FJ 列表的第一个数字(数字1)之后会得到一个剩下的数字2,2,1,3 组成的列表,并且符合一个可行的朋友关系配对——例如,如果我们将奶牛从A
到D 命名,那么配对(A;B); (A;C); (A;D) 以及(B;C) 就足够了,这是因为A有三个朋友,B 和C 都各有两个朋友,还有D 有一个朋友。同理,从FJ 的列表里移除另外一个“1”也是可行的,还有移除“3”也可以。从FJ 的列表里移除任意一个“2”都不可——我们可以看到剩下数字的和是奇数,明显我们无法凑出一个对应的配对。

做法:考虑把删除一个数后剩下是否符合,比较简单可以想到排序,但是全部用快排会T?(c++开O2优化能过233333),于是我们想到,其实并不用每次都快排,对于减了的一部分和没减的部分显然它们都是有序的,所以对它们做一次归并就好啦。

代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define rep(i, a, b)    for (int i = a; i <= b; i++)
#define N 507
using namespace std;
struct arr{
    int num, v;
}a[N];
int b[N], ans[N], tot, s, t1, t2, c[N], n;
bool ri = false;

bool cmp(arr x, arr y){
    return x.v > y.v;
}

void solve1(){
    int number = b[1];
        rep(i, 2, number + 1){
            if (b[i]){
                b[1]--, b[i]--;
                    if (!b[i])  t1++;
            } else {
                ri = 1;
                    return;
            }
        }
            int t = number + 2, cnt = 0;
                rep(i, 2, number - t1 + 1){
                    while (b[t] > b[i]) c[++cnt] = b[t++], b[t - 1] = 0;
                        c[++cnt] = b[i], b[i] = 0;
                }
                    rep(i, t, s)
                        c[++cnt] = b[i], b[i] = 0;
                            s = s - 1 - t1;
                                return;
}

void solve2(){
    int number = c[1];
        rep(i, 2, number + 1){
            if (c[i]){
                c[1]--, c[i]--;
                    if (!c[i])  t2++;
            } else {
                ri = 1;
                    return; 
            }
        }
            int t = number + 2, cnt = 0;
                rep(i, 2, number - t2 + 1){
                    while (c[t] > c[i]) b[++cnt] = c[t++], c[t - 1] = 0;;
                        b[++cnt] = c[i], c[i] = 0;;
                }
                    rep(i, t, s)
                        b[++cnt] = c[i], c[i] = 0;;
                            s = s - 1 - t2;
                                return;

}

bool work(){
    ri = false, s = n;
        while (!ri){
            t1 = 0, t2 = 0;
                solve1();
                    if (ri) return false;
                        if (s == 0) return true;
                            solve2();
                                if (ri) return false;
                                    if (s == 0) return true;
        }
    return true;
}

int main(){
    scanf("%d", &n);
        rep(i, 1, n + 1){
            scanf("%d", &a[i].v);
                a[i].num = i;
        }
            sort(a + 1, a + n + 2, cmp);
                rep(p, 1, n + 1){
                    s = 0;
                        rep(j, 1, n + 1)
                            if (j != p) b[++s] = a[j].v;
                                if (work()) ans[++tot] = a[p].num;
                }
    printf("%d\n", tot);
        sort(ans + 1, ans + tot + 1);
            rep(i, 1, tot)
                printf("%d\n", ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值