Ozon Tech Challenge 2020 (Div.1 + Div.2)
A
签到
B
给你一串有括号组成的字符串,要求你每次删去一个简单括号序列,要求你用最少的次数把字符串里的括号删完(简单括号序列:前n个为‘(’,后n个为’)‘);
思路:直接贪心字符串里的第一个括号(匹配字符串里的最后一个括号),这样匹配第一次肯定是删掉最长的,同时他删一次后就找不到其他的了
#include <bits/stdc++.h> using namespace std; const int maxn=4e5+10; typedef long long ll; int n,m,T,sum,piuy; int cnt,resss,cntt; string SSSS,ss; int ans[maxn],b[maxn],kk[maxn],laa[maxn],poo[maxn]; bool kop(int a,int b){ return a>b; } int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } void ini() { int pp=0; int ppp=0; for(int i=1;i<=10;i++)pp++; for(int i=10;i<=15;i++) ppp++; } void run() { for(int i=0;i<SSSS.size();i++) { if(SSSS[i]==')') poo[++cnt]=i+1; else laa[++resss]=i+1; } sort(poo+1,poo+1+cnt,kop); sort(laa+1,laa+1+resss); int i=1,j=1; while(1){ if(j>resss||cnt<i) break; if(laa[i]<poo[j]) { kk[++cntt]=laa[i]; kk[++cntt]=poo[j]; i++,j++; piuy=piuy+2; } else break; } if(piuy==0) puts("0"); else{ puts("1"); cout<<piuy<<endl; sort(kk+1,kk+1+cntt); for(int i=1;i<=cntt;i++) cout<<kk[i]<<" "; puts(""); } } int main() { ini(); cin>>SSSS; run(); }
C
由抽屉原理可知,当n>mn>m时,必定存在两个aiai模mm同余,此时答案为00
否则可以直接On2暴力
#include <bits/stdc++.h> using namespace std; const int maxn=2e5+11; typedef long long ll; int cnt,resss,cntt; string SSSS,ss; int ans[maxn],b[maxn],kk[maxn],laa[maxn],poo[maxn]; ll a[maxn],mod,n; int mpp[maxn]; bool kop(int a,int b){ return a>b; } ll read(){ char c=getchar();ll x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } void ini() { int pp=0; int ppp=0; for(int i=1;i<=10;i++)pp++; for(int i=10;i<=15;i++) ppp++; } int run() { n=read(),mod=read(); // rep(i,1,n) a[i]=i;//scanf("%lld",&a[i]); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++){ if(mpp[a[i]%mod]){ puts("0"); return 0; } ++mpp[a[i]%mod]; } ll ress=1; for(int i=1;i<=n;++i){ for(int j=1+i;j<=n;j++){ ress=ress*(a[i]-a[j])%mod; } } if(ress<=-1) ress=-ress; cout<<ress<<endl; } int main() { ini(); run(); }
D
给你一棵树,求出他的根,你可以询问u,v的lca最多n/2次
可以发现一棵树度数为1的点至少会有一个,因此可以每次挑出两个度数为1的点x,yx,y进行询问,若结果等于其中一个点,则其必定为根。否则删除这两个点并把新出现的度数为1的点加入集合,最坏情况就是恰好询问⌊n2⌋⌊n2⌋次
#include <bits/stdc++.h> using namespace std; #define TLE std::ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL); #define ll long long #define pb push_back typedef pair<int,int> pii; const ll mod = 1000000000 ; const int INF=0x3f3f3f3f; using namespace std; const int mxn = 3e5+7 ; int n,t,m,k,l,r,res,prime[mxn],isprime[mxn],phi[mxn],dis[mxn],head[mxn]; string str; vector<int>ans[mxn]; queue<int>q; int cnt[mxn]; bool vis[mxn]; void del(int x) { for(auto i : ans[x]) { cnt[i]--; if(cnt[i]==1) q.push(i); } } int main() { TLE; scanf("%d",&n); for(int i=1,u,v;i<n;i++) { scanf("%d %d",&u,&v); ans[u].pb(v);ans[v].pb(u); cnt[u]++;cnt[v]++; } for(int i=1;i<=n;i++) if(cnt[i]==1) q.push(i); while(q.size()>=2) { int u = q.front() ;q.pop(); int v = q.front() ;q.pop(); vis[u] = vis[v] = 1 ; printf("? %d %d\n",u,v); fflush(stdout); cin>>k; if(k==u || k==v ) {printf("! %d\n",k); return 0;} del(u);del(v); } k=0; for(int i=1;i<=n;i++) if(!vis[i]) printf("! %d\n",i) , k++; // assert(k==1); return 0 ; }
E
构造的话直接贪心构造。
先123456...,最后补差就行。
因为从3开始贡献依次为1,1,2,2,3,3,4,4,
当最后一个的贡献大于我们需要的,也就是我们必须弄出恰好m种,而你继续加的话贡献又会大于m,不加又小于
我们发现对这个元素x加1,他的贡献就会减一,所以不段++直到满足,后面的直接从1e9补回来
#include <iostream> #include <algorithm> #include <cstring> #include <vector> #include <cmath> #include <set> #include <map> #include <queue> #include <iomanip> #include <assert.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f #define Local #ifdef Local #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0) void err() { std::cout << '\n'; } template<typename T, typename...Args> void err(T a, Args...args) { std::cout << a << ' '; err(args...); } template <template<typename...> class T, typename t, typename... A> void err(const T <t> &arg, const A&... args) { for (auto &v : arg) std::cout << v << ' '; err(args...); } #else #define dbg(...) #endif using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 5000 + 5; int n, m; void run() { cin >> n >> m; if(n == 1) { if(m == 0) cout << 1 << '\n'; else cout << -1 << '\n'; return; } vector <int> a; a.push_back(1); a.push_back(2); int now = 1, cnt = 0; int t = 0, p = 3; while(t < m) { if(t + now > m) { int r = m - t; --p; for(int i = p + 1;; i++) { int L = i - p, R = p; if((R - L + 1) / 2 == r) { a.push_back(i); t += r; break; } } } else { a.push_back(p++); t += now; if(++cnt == 2) { ++now, cnt = 0; } } } if(sz(a) > n) { cout << -1 << '\n'; return; } int s = 1e9; while(sz(a) < n) { a.push_back(s); s -= 50000; } sort(all(a)); for(auto it : a) cout << it << ' '; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); run(); return 0; }
F
你有n个数, 你可以进行让某个数加一或减一的操作, 求最少的操作次数让这些数最大公因数不为1
思路,我们先假设gcd最少为2时候,那么最多操作只有n次对吧,因为gcd=2说明全是偶数,序列全是奇数我们只需每个位置操作1次,总共就n次。
在最多操作只有n次的条件下,那么一个位置操作数>=2的数量就是<=n/2,反过来说一个位置操作数小于等于1的位置个数就是大于等于n/2,那么也就是我们随机选择一个位置他操作个数小于等于1的几率起码是1/2,那么随机抽取20次,抽到操作个数大于等于2的就得是1/(2^20),忽略不计,所以枚举20次必定找到一个操作数小于等于1的
,那么找到这个数字后对他操作就是三个,-1,0,+1,三种情况分别判断一下,看哪个操作是最好次数就行
然后就行对这个数字的质因数进行判断看哪个质因数是最优的,因为所有数字的gcd,就是他们公共质因数
#include <iostream> #include <cstdio> #include <cstring> #include <map> #include <algorithm> #define ll long long using namespace std; template <typename T> void read(T &x) { x = 0; bool f = 0; char c = getchar(); for (;!isdigit(c);c=getchar()) if (c=='-') f=1; for (;isdigit(c);c=getchar()) x=x*10+(c^48); if (f) x=-x; } template <typename T> void write(T x) { if (x < 0) putchar('-'), x = -x; if (x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 205000; ll a[N], ans, n; map<ll, int> mp; void check(ll k) { ll res = 0; if (mp[k]) return; mp[k] = 1; for (int i = 1;i <= n; i++) { if (a[i] >= k) res += min(a[i] % k, k - a[i] % k); else res += k - a[i]; if (res >= ans) return; } ans = min(ans, res); } void work(ll x) { for (ll i = 2;i * i <= x; i++) { if (x % i) continue; while (x % i == 0) x /= i; check(i); } if (x > 1) check(x); } #include <cstdlib> #include <ctime> int main() { // freopen ("hs.in","r",stdin); // srand(time(0)); read(n); ans = n; for (int i = 1;i <= n; i++) read(a[i]); random_shuffle(a + 1, a + n + 1); int lim = min(n, 300ll); for (int i = 1;i <= lim; i++) { ll x = a[i]; work(x), work(x + 1); if (x > 1) work(x - 1); } cout << ans << endl; return 0; }