The Preliminary Contest for ICPC Asia Xuzhou 2019

A Math Problem

题目链接
题意:裸的 扩展欧几里得+斐波那契博弈
思路:模板题

#include <bits/stdc++.h>

#define maxn 15
typedef long long ll;
using namespace std;

int n;
ll GCD;
ll bi[maxn],ai[maxn];

ll gcd(ll a,ll b) {while(b^=a^=b^=a%=b);return a;}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x=1;y=0;return a;}
    ll gcd=exgcd(b,a%b,x,y);
    ll tp=x;
    x=y; y=tp-a/b*y;
    return gcd;
}

ll qmul(ll a,ll b,ll m){
    ll ans=0;
    ll k=a;
    ll f=1;//f是用来存负号的
    if(k<0){f=-1;k=-k;}
    if(b<0){f*=-1;b=-b;}
    while(b){
        if(b&1)
            ans=(ans+k)%m;
        k=(k+k)%m;
        b>>=1;
    }
    return ans*f;
}

ll excrt()
{
    ll x,y,k;
    ll M=bi[1],ans=ai[1];
    //bi被余数,ai余数
    for(int i=2;i<=n;i++)
    {
        ll a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;
        ll gcd=exgcd(a,b,x,y),bg=b/gcd;
        if(c%gcd!=0) return -1; 

        x=qmul(x,c/gcd,bg);//快乘
        ans+=x*M;
        M*=bg;
        ans=(ans%M+M)%M;
    }
    return (ans%M+M)%M;
}

void input(){
    scanf("%d",&n);

    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&bi[i],&ai[i]);

        if(i==1)
            GCD=bi[i];
        else
            GCD=GCD*bi[i]*gcd(GCD,bi[i]);
    }
}

vector <ll>fab;
void solve(){
    ll ans=excrt();
//    cout<<ans<<endl;
    if(ans!=-1){
        if(ans<=1)
        ans=GCD+ans;

        fab.push_back(1);
        fab.push_back(2);

        int i=2;
        int flag=0;
        if(ans==1||ans==2)
            flag=1;

        while(fab[i-2]+fab[i-1]<=ans){
            if(fab[i-2]+fab[i-1]==ans){
                flag=1;
                break;
            }

            fab.push_back(fab[i-2]+fab[i-1]);
            i++;
        }

        if(flag)
            printf("Lbnb!\n");
        else
            printf("Zgxnb!\n");
    }else
        printf("Tankernb!\n");
}

int main(){
    input();
    solve();
}

B so easy

题目链接
题意:1-n一共n个值,操作1、选择一个数,使其不可用。操作2、查找一个数,它或者它之后第一个可用的数
思路:并查集连接可行、不可行,通过unordered_map维护

#include <bits/stdc++.h>

using namespace std;

int n, q;
int u, v;
unordered_map <int, int>ma;

int find(int x) {
    if (!ma.count(x)) {
        return x;
    }
    else
        return ma[x] = find(ma[x]);

}

void mix(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    ma[fx] = fy;
}

void input() {
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= q; i++) {
        scanf("%d%d", &u, &v);
        if (u == 1)
            ma[v] = find(v + 1);
        else {
            int tmp = find(v);
            if (tmp > n)puts("-1");
            else printf("%d\n", tmp);
        }
    }
}

int main() {
    input();
    //    solve();
}

C Buy Watermelon

题目链接
题意:给定w表示西瓜重量,切成两半,要求每一部分都是2的倍数,输出yes、no
思路:签到

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
int main(void){
    int a;
    scanf("%d",&a);
    if(a % 2 == 0&&a > 2){
        puts("YES");
    }
    else{
        puts("NO");
    }
    return 0;
}

D Carneginon

题目链接
KMP相关,zl所A。

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
char x[maxn], y[maxn], z[maxn];
int nextt[maxn], ans[maxn];
int m, n;
void prekmp() {
    int i, j;
    j = nextt[0] = -1;
    i = 0;
    while (i < m) {
        while (j != -1 && x[i] != x[j]) {
            j = nextt[j];
        }
        if (x[++i] == x[++j]) {
            nextt[i] = nextt[j];
        }
        else {
            nextt[i] = j;
        }
    }
}
int kmp() {
    prekmp();
    int i, j, answer;
    i = j = answer = 0;
    while (i < n) {
        while (j != -1 && x[j] != y[i]) {
            j = nextt[j];
        }
        i++;
        j++;
        if (j == m) {
            j = nextt[j];
            ans[answer++] = i;
        }
    }
    return answer;
}

void prekmp1() {
    int i, j;
    j = nextt[0] = -1;
    i = 0;
    while (i < n) {
        while (j != -1 && y[i] != y[j]) {
            j = nextt[j];
        }
        if (y[++i] == y[++j]) {
            nextt[i] = nextt[j];
        }
        else {
            nextt[i] = j;
        }
    }
}
int kmp1() {
    prekmp1();
    int i, j, answer;
    i = j = answer = 0;
    while (i < m) {
        while (j != -1 && y[j] != x[i]) {
            j = nextt[j];
        }
        i++;
        j++;
        if (j == n) {
            j = nextt[j];
            ans[answer++] = i;
        }
    }
    return answer;
}

int main(void) {
    int t, i, q, len1, len, num;
    scanf("%s", y);
    scanf("%d", &q);
    n = len = strlen(y);
    while (q--) {
        scanf("%s", x);
        m = len1 = strlen(x);
        if (len1 < len) {
            num = kmp();
            if (num) {
                puts("my child!");
            }
            else {
                puts("oh, child!");
            }
        }
        else if (len1 > len) {
            num = kmp1();
            if (num) {
                puts("my teacher!");
            }
            else {
                puts("senior!");
            }
        }
        else {
            num = 1;
            for (i = 0; i < len; i++) {
                if (x[i] != y[i]) {
                    num = 0;
                    break;
                }
            }
            if (num) {
                puts("jntm!");
            }
            else {
                puts("friend!");
            }
        }
    }
    return 0;
}

E XKC's basketball team

题目链接
题意:对于每个数求,比它大m且最远的位置
思路:线段树暴力

#include <bits/stdc++.h>

#define maxn 500005
using namespace std;

int n, m;
int a[maxn];
int maxx[maxn << 2];

void PushUp(int rt) {
    maxx[rt] = max(maxx[rt << 1], maxx[rt << 1 | 1]);
}

void Build(int l, int r, int rt) {
    if (l == r)
    {
        maxx[rt] = a[l];
        return;
    }

    int mid = (l + r) >> 1;
    Build(l, mid, rt << 1);
    Build(mid + 1, r, rt << 1 | 1);
    PushUp(rt);
}

int query(int val, int l, int r, int rt) {

    //cout << val << " " << l << " " << r << endl;
    if (l == r)
        return l;

    int mid = (l + r) >> 1;

    if (maxx[rt << 1 | 1] >= val)
        return query(val, mid + 1, r, rt << 1 | 1);

    if (maxx[rt << 1] >= val)
        return query(val, l, mid, rt << 1);

    return -1;
}

void input() {
    scanf("%d%d", &n, &m);
    int i;
    for (i = 1; i <= n; i++)
        scanf("%d", &a[i]);
}

int isk = 0;
void p(int x) {
    if (!isk) {
        isk = 1;
        printf("%d", x);
    }
    else
        printf(" %d", x);
}

void solve() {
    isk = 0;
    Build(1, n, 1);
    int i;
    for (i = 1; i <= n; i++) {
        int ANS = query(a[i] + m, 1, n, 1);
        if (ANS <= i)p(-1);
        else p(ANS-i-1);
    }
}

int main() {
    input();
    solve();
}

G Colorful String

题目链接
回文树相关,lzk所A

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e5 + 10;
ll ans;

struct PAM {//回文树
    set<int>cor[maxn];
    int num[maxn];
    // 一个结点一个本质不同的回文串 
    int next[maxn][26];// next[i][c]编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号(儿子)。
    int fail[maxn];// fail[i] 节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串
    int len[maxn];//len[i] 表示回文串i的长度 
    int cnt[maxn];//cnt[i] 节点i表示的本质不同的串的个数
    int S[maxn];// S[i] 第i次添加的字符(一开始设S[0] = -1,也可以是任意一个在串S中不会出现的字符)

                //(建树时求出的不是完全的,最后重新统计一遍以后才是正确的)。 

    int id;//2~id-1 添加的结点 n 添加的字符个数
    int n, last;//last 新添加一个字母后所形成的最长回文串表示的节点
    int newnode(int x) {
        for (int i = 0; i<26; i++) {
            next[id][i] = 0;
        }
        num[id] = 0;
        cnt[id] = 0;
        len[id] = x;
        cor[id].clear();
        return id++;
    }
    void init(int le) {
        memset(cnt, 0, sizeof(cnt));
        id = 0;
        newnode(0);
        newnode(-1);
        fail[0] = 1;
        S[0] = -1;
        last = n = 0;
    }
    int getfail(int x) {
        while (S[n - len[x] - 1] != S[n]) x = fail[x];
        return x;
    }
    void Insert(int c) {
        c -= 'a';
        S[++n] = c;
        int cur = getfail(last);
        if (!next[cur][c]) {
            int now = newnode(len[cur] + 2);
            fail[now] = next[getfail(fail[cur])][c];
            next[cur][c] = now;
            //
            num[now] = num[fail[now]] + 1;
        }
        last = next[cur][c];
        cor[last] = cor[cur];
        //cout << c << endl;
        cor[last].insert(c);
        cnt[last]++;
    }
    void getAns() {//自下向上更新
        for (int i = id - 1; i >= 0; i--) {
            cnt[fail[i]] += cnt[i];
            ans += 1ll * cnt[i] * ((int)cor[i].size());
            //cout << "i:  " << i << "  cnt[i]:" << cnt[i] << " cor.size(): " << cor[i].size() << endl;
        }
    }
}pam;
char s[maxn];

int main() {
    while (~scanf("%s", s)) {
        ans = 0;
        int len = strlen(s);
        pam.init(len);
        for (int i = 0; i < len; i++) {
            pam.Insert(s[i]);
        }
        pam.getAns();
        printf("%lld\n", ans);
    }
}

I query

题目链接
题意:给定一个数列,求区间[l,r]包含多少个min(pi,pj)=gcd(pi,pj)
思路:nlogn找出倍数关系,然后下标离散,建立主席树

#include <bits/stdc++.h>

#define maxn 1000005
#define N 100005
typedef long long ll;
using namespace std;

int n,m;
int a[N];
int ind[N];

struct node{
    int l,r;
    int cnt;
    node(){
        l=r=cnt=0;
    }
}T[maxn*18];

int rt[maxn];
int xCopy[maxn];
int tot=0;
int ans=0;

inline void update(int &x,int y,int l,int r,int pos,int val){
    T[++tot]=T[y];
    T[tot].cnt+=val;
    x=tot;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(T[x].l,T[y].l,l,mid,pos,val);
    else
        update(T[x].r,T[y].r,mid+1,r,pos,val);
}

inline void query(int x,int y,int l,int r,int L,int R){
    if (L <= l && r <= R) {
        ans += T[y].cnt - T[x].cnt;
        return;
    }
    int mid = l + r >> 1;
    if (L <= mid)query(T[x].l, T[y].l, l, mid, L, R);
    if (R > mid)query(T[x].r, T[y].r, mid + 1, r, L, R);
}

inline void input(){
    scanf("%d%d",&n,&m);
    int i,j;
    int IND;
    for(i=1;i<=n;++i){
        scanf("%d",&a[i]);
//        a[i] = i;
        if(a[i]==1)
            IND=i;
        ind[a[i]]=i;
    }

    int cnt=1,tmp;
    for(i=1;i<=n;++i){
        if(a[i]>n/2||a[i]==1)continue;
        tmp=a[i];
        for(j=tmp*2;j<=n;j+=tmp){
            update(rt[cnt],rt[cnt-1],1,n,ind[j],1);
            xCopy[cnt]=ind[a[i]];
//            cout<<ind[a[i]]<<endl;
            ++cnt;
        }    
    }    
//    cout<<cnt<<endl;

//    for(i=1;i<cnt;i++)
//        cout<<xCopy[i]<<endl;

    int x,y,pos1,pos2;
    for(i=1;i<=m;++i){
        scanf("%d%d",&x,&y);

        pos1=lower_bound(xCopy+1,xCopy+cnt,x)-xCopy;
        pos2=upper_bound(xCopy+1,xCopy+cnt,y)-xCopy-1;

//        cout<<"!"<<pos1<<" "<<pos2<<endl;
        ans=0;
        query(rt[pos1-1],rt[pos2],1,n,x,y);
        if(x<=IND&&IND<=y)
            ans+=y-x;
        printf("%d\n",ans);
    }
}

int main(){
    input();
}

/*
5 100
5 4 3 2 1

5 100
4 1 3 2 5
*/

做法是,将询问和更新都变为[l,r]形式的二元组存储,对于这样二元组排序,先按右区间排序,保证区间尽量靠左端,再按左区间排序保证区间尽量小,最后保证询问再更新之后。树状数组维护二维偏序关系。

#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

int n,m;
int a[maxn];
int ind[maxn];
int ans[maxn];
int c[maxn];

struct node{
    int flag;
    int id;
    int l,r;
};

vector <node>v;

int lowbit(int x){
    return x&(-x);
}

void add(int i,int val){
    while(i<=n){
        c[i]+=val;
        i+=lowbit(i);    
    } 
}

int sum(int i){
    int ans=0;
    while(i){
        ans+=c[i];
        i-=lowbit(i);
    }

    return ans;
}

bool cmp(node a,node b){
    if(a.r==b.r)
    {
        if(a.l==b.l)
            return a.flag<b.flag;
        else
            return a.l>b.l;
    }else
        return a.r<b.r;
}

void input()
{
    scanf("%d%d",&n,&m);
    int i,j;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        ind[a[i]]=i;
    }

    for(i=1;i<=n;i++){
        for(j=i+i;j<=n;j+=i){
            int a=ind[i];
            int b=ind[j];

            if(a>b)
                swap(a,b);

            v.push_back((node){1,0,a,b});
        }
    }

    int l,r;
    for(i=1;i<=m;i++){
        scanf("%d%d",&l,&r);
        v.push_back((node){2,i,l,r});
    }

    sort(v.begin(),v.end(),cmp);

    int tot=0;
    for(i=0;i<v.size();i++){
        if(v[i].flag==1){
            add(v[i].l,1);
            tot++;
        }else
            ans[v[i].id]=tot-sum(v[i].l-1);
    }

    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
}


int main(){
    input();
//    solve();
}

J Random Access Iterator

题目链接
树形dp,zl、lzk所A

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
vector<int>v[maxn];

int dep[maxn];
int ans[maxn];
int MA_dep = 0;

void dfs(int x, int fa) {
    for (int y : v[x]) {
        if (y == fa)continue;
        dep[y] = dep[x] + 1;
        MA_dep = max(MA_dep, dep[y]);
        dfs(y, x);
    }
}

ll qpow(ll A, int B) {
    ll t = 1;
    while (B) {
        if ((B & 1)) {
            t = t * A%mod;
        }
        A = A * A%mod;
        B = B >> 1;
    }
    return t;
}

ll dfs1(int x, int fa) {
    if (dep[x] == MA_dep) {
        return 1LL;
    }
    ll res = 0;
    for (int y : v[x]) {
        if (y == fa)continue;
        res += dfs1(y, x);
    }
    int s = v[x].size();
    if (x != 1) {
        s--;
    }
    res = res * qpow(s, mod - 2) % mod;
    res = (1 - res + mod) % mod;
    res = qpow(res, s);
    res = (1 - res + mod) % mod;
    return res;
}


int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        v[x].push_back(y); swap(x, y);
        v[x].push_back(y);
    }
    dep[1] = 1;
    dfs(1, -1);
    ll ans=dfs1(1, -1);
    printf("%lld\n", ans);
}

K Center

题目链接
状压dp,规律,zl、lzk所A

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int maxn = 1e3 + 5;
int x[maxn], y[maxn];
const int M = 1e7;
unordered_map<ll, set<int> >mp;

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x[i], &y[i]);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            int xx = x[i] + x[j];
            int yy = y[i] + y[j];
            ll num = xx * M + yy;
            mp[num].insert(i);
            mp[num].insert(j);
        }
    }
    for (auto p : mp) {
        int sz = (int)p.second.size();
        ans = max(ans, sz);
    }
    printf("%d\n", n - ans);
}

M Longest subsequence

题目链接
dp,zl、lzk所A

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
int poss[32];
int n, m;
char s[maxn * 10], t[maxn * 10];
int main(void){
    int i, j = 0, k, ans = -1;
    scanf("%d %d",&n,&m);
    scanf("%s %s",s,t);
    for(i = 0;i < 26;i++){
        poss[i] = -1;
    }
    for(i = 0;i < n;i++){
        for(k = 0;k < s[i] - 'a';k++){
            if(poss[k] != -1){
                ans = max(ans,n - i + poss[k]);
            }
        }
        if(j == m||s[i] > t[j]){
            ans = max(ans,n - i + j);
            if(j < m - 1){
                j++;
            }
        }
        else if(s[i] == t[j]){
            poss[s[i] - 'a'] = j;
            j++;
        }
    }
    printf("%d\n",ans);
    return 0;
}
/*
3 2
abc
ab
*/