Ozon Tech Challenge 2020 (Div.1 + Div.2)
Ozon Tech Challenge 2020 (Div.1 + Div.2)
题目链接:https://codeforces.com/contest/1305
A. Kuroni and the Gifts
怎么做都能AC。
B. Kuroni and Simple Strings
题解:
要么0次,要么1次。从最外层开始去除,一定是最优的。因为最左边的"("可以匹配右边所有的")",而最右边的")"可以匹配左边的"("。也就是两边的括号能匹配的更多,所以需要优先去除。暴力就能过。
代码:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
const int INF=1e9+7;
const int N=5050;
int n,f[N],len;
char s[N];
bool dfs(set<int> &a){
int l=len+1,r=0;
for(int i=1;i<len;i++){
if (f[i]==0 && s[i]=='('){
l=i;
f[i]=1;
break;
}
}
for(int i=len-1;i>=1;i--){
if (f[i]==0 && s[i]==')'){
r=i;
f[i]=1;
break;
}
}
if (l>r)return 0;
a.insert(l);
a.insert(r);
return 1;
}
void work(){
scanf("%s",s+1);
s[0]='a';
len=strlen(s);
vector<set<int>> ans;
set<int>a;
while(dfs(a));
if (a.size()==0){printf("0\n");return ;}
printf("1\n");
printf("%d\n",(int)a.size());
for(auto x:a){
printf("%d ",x);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
work();
}
C. Kuroni and Impossible Calculation
题解
千万不要把他当做数论去想。关键就在模数很小只有1000。当\(n>m\)时,那肯定存在\((a[i]-a[j])\%m==0\) 。要是能想到这里,这道题就没了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1050;
int a[N];
int main(){
int n,mo,ans=1;
scanf("%d %d",&n,&mo);
if (n>mo){cout<<0<<endl;return 0;}
for(int i=1;i<=n;i++){
scanf("%d",a+i);
for(int j=1;j<i;j++){
ans*=abs(a[j]-a[i])%mo;
ans%=mo;
}
}
cout<<ans<<endl;
}
D. Kuroni and the Celebration
题解
我用的方法感觉比较玄学,一直WA来WA去。先随机选一个数为根,然后每次询问两个子节点,如果\(lca\)为子节点,那么实际的根肯定在子树里面。如果儿子都不是\(lca\),那么父亲就是根。这样做,有一个小问题,就是如果儿子是奇数的话,就会单出来一个,这样询问次数就可能就会超过\(\lfloor\frac{n}{2}\rfloor\)。那么就选择\(size[x]\approx[\frac{n}{2}],且son[x]\%2==0的点为根(size[x]为x子树的大小,son[x]表示x的儿子的个数)\)
还有就是每次先询问\(size\)更大的儿子。反正这些方法都比较玄学,要考虑会被什么数据卡,可麻烦了。
正解其实是类似拓扑排序,每次找两个度为1的点记为\(x,y\),然后询问,如果$lca(x,y)==x \(,那么\)x\(就是根,如果\)x,y$都不是根那就删除这两点,同时更新与之想连的点的度。这样能保证每次删除两个点。最后要么在中途找到,要么只剩下一个点。也就做完啦,感觉很好写。
代码一 玄学dfs
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
const int INF=1e9+7;
const int N=5050;
int n,size[N],d[N],Tot,f[N];
struct Edge{int x,y,s;};
bool cmp(int x,int y){return size[x]>size[y];}
struct Graph{
int tot=0,last[N];
Edge edges[N<<1];
void addEdge(int x,int y){
edges[++tot]={x,y,last[x]};
last[x]=tot;
}
void dfs1(int x,int pre){
size[x]=1;
for(int i=last[x];i;i=edges[i].s){
Edge &e=edges[i];
if (e.y==pre)continue;
dfs1(e.y,x);
size[x]+=size[e.y];
}
}
void dfs(int x,int pre){
static int a[N];
int lca,num=0;
for(int i=last[x];i;i=edges[i].s){
Edge &e=edges[i];
if (e.y==pre)continue;
a[++num]=e.y;
}
a[num+1]=x;
sort(a+1,a+num+1,cmp);
for(int i=1;i<=num;i+=2){
//fflush(stdout);
printf("? %d %d\n",a[i],a[i+1]);
Tot++;
if (Tot>n/2)cout<<a[1000000];
fflush(stdout);
scanf("%d",&lca);
//fflush(stdout);
if (lca!=x){
dfs(lca,x);
return;
}
}
printf("! %d\n",x);
return;
}
}G;
void work(){
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int x,y;
scanf("%d %d",&x,&y);
d[x]++; d[y]++;
G.addEdge(x,y);
G.addEdge(y,x);
}
int mx=0,xx=0,yy=1;
G.dfs1(1,0);
for(int i=1;i<=n;i++){
if (d[i]%2==0) yy=i;
if (size[i]<=(n+1)/2 && d[i]%2==0 && size[i]>mx){
mx=size[i];
xx=i;
}
}
if (xx==0)xx=yy;
G.dfs1(xx,0);
G.dfs(xx,0);
}
int main(){
#ifndef ONLINE_JUDGE
//freopen("aa.in","r",stdin);
#endif
work();
}
代码二 拓扑序
#include<bits/stdc++.h>
using namespace std;
const int N=1050;
int n;
struct Edge{int x,y,s;};
struct Graph{
int d[N],last[N],tot=0;
Edge edges[N<<1];
void addEdge(int x,int y){
edges[++tot]=Edge{x,y,last[x]};
last[x]=tot;
d[x]++; d[y]++;
}
void addNode(queue<int> &Q, int x){
for(int i=last[x];i;i=edges[i].s){
Edge &e=edges[i];
d[e.y]-=2;
if (d[e.y]==2){Q.push(e.y);d[e.y]=-1;}
}
}
void solve(){
queue<int>Q;
for(int i=1;i<=n;i++){
if (d[i]==2){d[i]=-1;Q.push(i);}
}
while(!Q.empty()){
int x=Q.front(); Q.pop();
addNode(Q,x);
if (Q.empty()){
printf("! %d\n",x);
fflush(stdout);
return;
}
int lca,y=Q.front(); Q.pop();
addNode(Q,y);
printf("? %d %d\n",x,y);
fflush(stdout);
scanf("%d",&lca);
if (lca==x || lca==y){
printf("! %d\n",lca);
return;
}
}
}
}G;
int main(){
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
G.addEdge(x,y);
G.addEdge(y,x);
}
G.solve();
}
E. Kuroni and the Score Distribution
题解
我们先想怎样可以使得m很快被满足。因为序列单增,所以第\(i\)位,最多有\(\frac{i-1}{2}\)个数满足条件。即\(a[1]+a[i-1]=a[2]+a[i-2]=...=a[\frac{i-1}{2}]+a[i-\frac{i-1}{2}]\)。
这个大概就是一个等差数列,于是就想着从\(1\)开始构建公差为\(1\)的等差数列。
那么如果超过了呢。假如到\(i\)位还需要\(x(x<\frac {i-1}{2})\)个三元组。那么只要令\(a[i]=a[i-1]+a[i-2*x]\)即可。
最后就是考虑剩下的不会再出现新的三元组。我们可以直接令\(a[i]=INF(INF表示一个很大的数,如10^8)\),然后之后每个数加上前面等差数列最大的那个值就行。假如前面枚举了五个数\(1,2,3,4,5\),那么之后就可以是\(100000,100006,100012...\)。然后就没了。一般构造题,方法都不只一种,没必要一定要构建一个最极限的方法。
#include<bits/stdc++.h>
using namespace std;
const int N=10050;
int ans[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
int Max=(n/2-1)*(n/2)+(n/2)*(n%2);
if (m>Max){printf("-1\n");return 0;}
if (m==0){
for(int i=n+1;i<=2*n;i++)printf("%d%c",i," \n"[i==2*n]);
return 0;
}
int num=0;
ans[++num]=1;
ans[++num]=2;
for(int i=3;i<=n;i++){
num++;
int val=(i-1)/2;
if (val<m){
m-=val;
ans[num]=ans[num-1]+ans[num-i+1];
}else{
ans[num]=ans[num-1]+ans[num-2*m];
m=0;
break;
}
}
ans[num+1]=100000000;
for(int i=num+2;i<=n;i++){
ans[i]=ans[i-1]+ans[num]+1;
}
for(int i=1;i<=n;i++)printf("%d%c",ans[i]," \n"[i==n]);
F. Kuroni and the Punishment
题解
想了半天,首先最容易发现,每个奇数加1,是一种可行方案,\(ans\le n\)。
之后想怎么枚举公约数,只要枚举质数就行了,但是显然没办法枚举太多。
然后又想,应该很多点是不会变动的。因为平均每个变动不到一次\((\frac{ans}{n}<1)\)。
于是就想随遍抽取两个数,把他们的最大公约数作为所有数的公约数,重复100次。然后WA了,但是总感觉就是一个随机抽取。于是又想啊想。终于明白了。
肯定至少有一半的数变动等于0或1。我们可以求变动为2的数最多为\(k\),则
\(2*k\le n\Rightarrow k\le \frac{n}{2}\)。也就是说一半以上都是变动1或0。那我们随便抽一个数,它变动大于1的概率是\(\frac{1}{2}\),我们抽取20次,一次都抽不到的概率则为\(\frac{1}{2^{20}}\),这个概率几乎是不可能。也就是说抽20次肯定能抽到变动为0或1的数。那我们就把每次抽到的数\(x,x\pm1\)进行质因数分解,再把这些质数当做公约数求一遍,取最小值即可。
其实抽10次就已经差不多了。我试了两次,第一次过了,第二次WA在160个点左右。
最后告诫大家最好不要用\(rand()\),因为它在codeforces上的范围是\([0,32767]\)。
具体大家可以看看:https://www.cnblogs.com/RabbitHu/p/10390146.html
代码
#include<bits/stdc++.h>
using namespace std;
#define gcd __gcd
//#define RAND_MAX 200050
typedef long long ll;
const int N=200050;
set<ll> S;
ll a[N],ans;
int n;
void dfs(ll x){
ll len=(ll)(sqrt(x));
for(ll i=2;i<=len;i++){
if (x%i==0){
S.insert(i);
while(x%i==0)x/=i;
}
}
if (x>1)S.insert(x);
}
void check(ll x){
ll tp=0;
for(int i=1;i<=n;i++){
if (a[i]<=x)
tp+=x-a[i];
else
tp+=min(a[i]%x,x-a[i]%x);
if (tp>=ans)return;
}
ans=tp;
}
int main(){
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
scanf("%d",&n);
ll x,y;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
if (a[i]&1)ans++;
}
for(int i=1;i<20;i++){
x=rng()%n+1;
dfs(a[x]);
dfs(a[x]+1);
if (a[x]>1)dfs(a[x]-1);
}
for(ll x:S)check(x);
printf("%lld ",ans);
}