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 */