Good Bye 2019

A. Card Game (CF 1270 A)

题目大意

两个人各持有\(k_1\)\(k_2\)张牌,牌序号唯一且在范围\(1\)~\(n\)其中\(n=k_1+k_2\)。一次操作两个各亮出一张牌,谁牌序号大,谁拿对方的牌,最后没牌者输,问先手是否必赢。

解题思路

很显然谁持有最大牌谁赢......

但是\(tourist\)能在一分钟内读完题并敲好代码提交过了真是太强了。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int n,k1,k2;

int main(void) {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        read(n);
        read(k1);
        read(k2);
        bool qwq=false;
        for(int u,i=1;i<=k1;++i){
            read(u);
            if (u==n) qwq=true;
        }
        for(int u,i=1;i<=k2;++i){
            read(u);
        }
        if (qwq) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}


B. Interesting Subarray (CF 1270 B)

题目大意

给定一个长度为\(n\)的数组\(a\),问是否存在某个区间\([l,r]\),有\(max-min\geq k\),其中\(max=\max\limits_{i \in [l,r]}(a_i)\)\(min=\min\limits_{i \in [l,r]}(a_i)\)\(k=r-l+1\),存在则输出\(YES\)并输出任意一个符合要求的区间,否则输出\(NO\).

解题思路

我们考虑一个符合题目条件的区间\([l,r]\),其中最大值和最小值在端点处是最优的选择,不失一般性,我们设最大值在\(r\)处,最小值在\(l\)处,则\(max-min=(max_1-max_2)+(max_2-max_3)+...+(max_{k-1}-max_k)\geq k\),从中我们可以看出至少有一个\(max_i-max_{i+1}>1\),才能有该不等式成立,那也一定有至少一个位置\(i\)\(abs(a_i-a_{i+1})\geq 2\)才行。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int n,u,v;

int main(void) {
    //ios::sync_with_stdio(false);
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        read(n);
        read(u);
        bool qwq=false;
        int l=0;
        for(int i=2;i<=n;++i){
            read(v);
            if (ABS(v-u)>1) {qwq=true;l=i-1;}
            u=v;
        }
        if (qwq) printf("YES\n%d %d\n",l,l+1);
        else printf("NO\n");
    }
    return 0;
}


C. Make Good (CF 1270 C)

题目大意

给定\(n\)个正整数,设\(n\)个数的和为\(sum\),异或为\(x\),若\(sum=2x\)则这个序列是\(good\)的,如果不是\(good\),最多可增加三个正整数,使得这个序列变成\(good\),求添加数的个数以及数的大小。

解题思路

首先\(sum\)一定要是偶数,否则我们先加一个数\(1\),然后再考虑\(sum\)\(x\)大小。

注意到异或两次同一个数相当于不变,那么如果\(sum<2x\),那么我们只要再增加两个同样的数\(qwq=\dfrac{2x-sum}{2}\),这样\(x\)不变,而\(sum\)也变成了\(2x\)

如果\(sum>2x\),我们再一开始增加一个超大超大的数,让\(sum<2x\)即可,由于\(sum\leq 10^{15}\),那我们就增加一个\(10^{16}\)的数,即\(2^{50}\),如果\(sum\)是奇数,就加\(2^{50}+1\),然后根据上面的情况即可。

对应代码为注释部分。

这题还有另一种加法,即加两个数\(x\)\(sum+x\),这样和就变成\(2(sum+x)\),异或值变成\(sum+x\),这样也符合题意。

还有最优的只用加一个数的方法(如果\(sum\neq x\))
即先\(x=2*x\)后,从低位到高位逐一比较\(sum\)\(x\)的每一位是否相等,如果不相等则添一个在该位为\(1\)的数,添这个数后只会影响\(sum\)\(x\)的高位,不会影响低位,然后把这些数加起来就得到我们要的那一个数了。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    //ios::sync_with_stdio(false);
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        int n;
        LL sum,xx,u,a;
        read(n);
        sum=xx=0;
        for(int i=1;i<=n;++i){
            read(u);
            sum+=u;
            xx=xx^u;
        }
        printf("2\n%lld %lld\n",xx,xx+sum); //两个数
        /* 
        一个数
        xx<<=1;
        LL ans=0;
        int id=0;
        while(sum||xx){
            if ((sum^xx)&1){
                ans+=(1ll<<id);
                sum++;
                xx^=2;
            }
            sum>>=1;
            xx>>=1;
            ++id;
        }
        if (ans) printf("1\n%lld\n",ans);
        else printf("0\n\n"); 
        */
        /* 
        三个数        
        if ((xx<<1ll)==sum) printf("0\n\n");
        else{
            a=(1ll<<50ll);
            if (sum&1ll) a=a|1ll;
            sum+=a;
            xx=(xx^a);
            xx<<=1;
            LL qwq=xx-sum;
            printf("3\n%lld %lld %lld\n",a,(qwq>>1),(qwq>>1));
        } 
        */
    }
    return 0;
}


D. Strange Device (CF 1270 D)

题目大意

交互题。

给定\(n\)\(k\),以及一个你并不知道长度为\(n\)的数俩俩不同的数列和\(m\),每次你可以问\(k\)个互不相同的下标,对方会告诉你这些下标对应的值里,从小到大排列第\(m\)个小数的位置和值。不能问多于\(n\)次。最后你的程序要猜出\(m\)值。

解题思路

一开始妄图想找到\(k\)个数然后问它们一波得到\(m\)值结果不可行。然后妄图用并查集维护每个数在\(m\)的左边还是右边发现数太多了不可控\(qwq\)所以我们要考虑少点的数,比如就前\(k+1\)个数,每次我们选这其中的\(k\)个数,相当于把其中的一个数从\(k+1\)个数中剔除,然后我们考虑这个剔除的数对第\(m\)个数的影响。

假设在这\(k+1\)个数中,第\(m\)个数的值为\(a\),则如果剔除的一个数\(i\)小于等于\(a\),则第\(m\)个数会变成另一个数\(b\),如果大于\(a\),则第\(m\)个数还是\(a\)。纵观我们剔除\(k+1\)次的结果,可以知道数\(a\)会出现\(k+1-m次\),而数\(b\)会出现\(m\)次,于是我们统计较大的数出现的次数就是答案\(m\)

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    //ios::sync_with_stdio(false);
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    int n,k;
    read(n);
    read(k);
    vector<bool> sign(k+2,false);
    int ma=0,cnt=0;
    for(int i=1;i<=k+1;++i){
        sign[i]=true;
        sign[i-1]=false;
        printf("? ");
        for(int j=1;j<=k+1;++j) if (!sign[j]) printf("%d ",j);
        puts("");
        fflush(stdout);
        int u,v;
        read(u);read(v);
        if (ma<v){cnt=1;ma=v;}else if (ma==v) ++cnt;
    }
    printf("! %d\n",cnt);
    fflush(stdout);
    return 0;
}


E. Divide Points (CF 1270 E)

题目大意

给出\(n\)个点坐标,要求将它们分成两个非空集\(A\)\(B\),使得没有同组的点距离与不同组的点距离相同。

解题思路

由于\(n\)不大,一开始想着枚举所有距离然后排序,把有相同距离的点对分到同一个组,然后构造了两个反例\(qwq\),一个是所有点分布呈以一个点为圆心的圆上,另一个是两个圆emmmmm。

我们试着根据点的坐标性质进行分类,所有点坐标在\(mod\ 2\)的意义下我们可以分成四类:\((0,0)\ (0,1)\ (1,0)\ (1,1)\),如果我们对于属于同类的点都放在一个集合里时,它们的\(x\)\(y\)坐标差一定是偶数,这样它们的距离的平方就一定是偶数,这启示我们,如果不同组的点距离的平方是奇数的话,那么就一定没有同组的点距离与不同组的点距离相同。经过分析我们可以知道,点坐标属于\((0,0)\)\((1,1)\)的点与点坐标属于\((1,0)\)\((0,1)\)的点的距离一定是奇数。那么我们就把点坐标属于\((0,0)\)\((1,1)\)放在\(A\),点坐标属于\((1,0)\)\((0,1)\)放在\(B\)即可。再综合一下就是点坐标和是偶数的放在\(A\),点坐标和是奇数的放在\(B\)

那要是没有点坐标和是奇数或偶数的怎么办?我们先选定一个点为原点进行整体平移,这样就一定有坐标和为偶数的点。如果没有坐标和是奇数点的话,此时所有点坐标和都为偶数,但我们注意到,如果点属于\((0,0)\)的,它们点的距离的平方是\(4\)的倍数,而点属于\((1,1)\)的,它们点的距离的平方除以4会余2,这样我们就把\((0,0)\)放在\(A\)\((1,1)\)放在\(B\)即可。若没有\((1,1)\)的点的话,我们对全部坐标都除以\(2\),这样所有点的距离的平方都会除以\(4\),但该相等的还是相等,不相等的还是不相等,不影响我们的判断。由于坐标最大不超过\(10^6\),故除以\(2\)的次数不会超过\(\log_2 10^6\),重复以上操作即可。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    /* int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        printf("Case #%d: ", i);
    } */
    int n;
    read(n);
    vector<int>x,y;
    for(int u,v,i=0;i<n;++i){
        read(u);read(v);
        x.push_back(u);
        y.push_back(v);
    }
    for(int i=1;i<n;++i){
        x[i]-=x[0];
        y[i]-=y[0];
    }
    x[0]=y[0]=0;
    while(1){
        vector<int> qwq;
        for(int i=1;i<n;++i) 
            if ((x[i]+y[i])&1) qwq.push_back(i);
        if (!qwq.empty()){
            printf("%d\n",(int)qwq.size());
            for(int i:qwq){
                printf("%d ",i+1);
            }
            puts("");
            return 0;
        }
        for(int i=1;i<n;++i)
            if (x[i]&1) qwq.push_back(i);
        if (!qwq.empty()){
            printf("%d\n",(int)qwq.size());
            for(int i:qwq){
                printf("%d ",i+1);
            }
            puts("");
            return 0;
        }
        for(int i=1;i<n;++i){
            x[i]>>=1;
            y[i]>>=1;
        }
    }
    return 0;
}


G. Subset with Zero Sum (CF 1270 G)

题目大意

给定一个长度为\(n\)的数组\(a\),其中第\(i\)个元素\(a_i\)满足:\(i-n\leq a_i\leq i-1\),请选择一些数使得它们和为\(0\),并输出它们的位置。保证有解,若有多解,输出任意一种即可。

解题思路

这题思路清奇......
我们将\(i-n\leq a_i\leq i-1\)变形得到\(1\leq i-a_i\leq n\)
注意到\(i-a_i\)仍在\([1,n]\)的范围,我们构造一张有向图,第\(i\)个点连向第\(i-a_i\)个点。由于每个点都有条出边,那么一定会有一个环,我们考虑环上的点\(i_1,i_2,i_3,...,i_k\),分别有:

  • \(i_1-a_{i_1}=i_2\)
  • \(i_2-a_{i_2}=i_3\)
  • \(i_3-a_{i_3}=i_4\)
  • \(i_4-a_{i_4}=i_5\)
  • ......
  • \(i_k-a_{i_k}=i_1\)

累加得\(a_{i_1}+a_{i_2}+a_{i_3}+......+a_{i_k}=0\)这样我们就找到了一组符合题目要求的\(i_1,i_2,i_3,...,i_k\)
(这脑洞是有多大……)

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        int n;
        read(n);
        vector<int> a(n+1);
        for(int i=1;i<=n;++i)
            read(a[i]);
        vector<bool> visit(n+1,false);
        int qwq=1;
        while(!visit[qwq]){
            visit[qwq]=true;
            qwq=qwq-a[qwq];
        }
        vector<int> ans;
        ans.push_back(qwq);
        int qaq=qwq-a[qwq];
        while(qaq!=qwq){
            ans.push_back(qaq);
            qaq=qaq-a[qaq];
        }
        printf("%d\n",(int)ans.size());
        for(int i:ans) printf("%d ",i);
        puts("");
    }
    return 0;
}


May 2020 will be the year of high ratings for the hard workers.

posted @ 2019-12-30 21:27  ~Lanly~  阅读(294)  评论(0编辑  收藏  举报