Codeforces Round #614 (Div. 2)

A. ConneR and the A.R.C. Markland-N

题解

拍完序之后往两边搜就行,不知道为啥,最近代码写的奇丑无比

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=1010;
int T,n,s,k,a[maxn];
int main()
{
	T=read();
	while(T--)
	{
		bool ok=0;
		n=read();s=read();k=read();
		for(int i=1;i<=k;i++)
		{
			a[i]=read();
			if(a[i]==s)ok=1;
		}
		if(!ok){printf("0\n");continue;}
		sort(a+1,a+k+1);
		int pos=0,ans=2147483647,tmp;
		for(int i=1;i<=k;i++)if(a[i]==s)pos=i;
		tmp=pos;
		while(tmp && s-a[tmp]==pos-tmp)tmp--;
		if(a[tmp+1]!=1)ans=pos-tmp;
		tmp=pos;
		while(tmp<=k && a[tmp]-s==tmp-pos)tmp++;
		if(a[tmp-1]!=n)ans=min(ans,tmp-pos);
		printf("%d\n",ans);
	}
	return 0;
}

B. JOE is on TV!

题解

\(f[i]\) 表示\(i\) 时候的最优解,转移很显然:$ f[i]=max (f[j]+1-j/i)$ ,然后再稍微动动脑子就会发现\(j=i-1\) 的时候最优,然后就递推\(O(n)\) 就好了

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
double f[100010]; 
int n;
int main()
{
	n=read();
	f[1]=1.0;
	for(int i=2;i<=n;i++)f[i]=f[i-1]+1.0-(double)(i-1)/(double)i;
	printf("%.10lf\n",f[n]);
	return 0;
}

C. NEKO's Maze Game

题解

之前很久以前做过一道类似的题,然后就往那个方向去想,结果越想越离谱,冷静一下发现这是DIV2T3
对于每一个修改成不能走的点,判一下他的(上或下)的左中右是否有块,表示堵住路的数量,对于修改成能走的,继续模仿上一步,有块的减去,表示这块路能走了。
这道题是\(n=2\) 的情况,我想了想,如果是一般情况的话就得用可持久化并查集维护上下的连通性了。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=100010;
int n,Q,x,y,blo[3][maxn],blocked;
int main()
{
	n=read();Q=read();
	while(Q--)
	{
		x=read();y=read();
		blo[x][y]^=1;
		if(blo[x][y])
		{
			for(int i=-1;i<=1;i++)
				if(y+i>=1 && y+i<=n && blo[3-x][y+i])blocked++;
		}
		else
		{
			for(int i=-1;i<=1;i++)
				if(y+i>=1 && y+i<=n && blo[3-x][y+i])blocked--;
		}	
		if(!blocked)printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

题解

发现系数最少就是2,这种指数型增长谁受得了,撑不到几十次就炸了。所以能走到的点很少,对于剩下的点暴力走一走就好了。
我的走法是从起点到任意一个点,然后分别往左走往右走统计最长路。
第一次还wa了,发现是阈值定小了。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline LL read()
{
	LL x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=10000;
LL X0,Y0,ax,ay,bx,by,xs,ys,t,x[maxn],y[maxn],xx[maxn],yy[maxn],End,len,ans,tans;
LL labs(LL a){return a<0 ? -a : a;} 
LL dis(LL a,LL b,LL c,LL d){return labs(c-a)+labs(d-b);}
int main()
{
	X0=read();Y0=read();ax=read();ay=read();bx=read();by=read();
	xs=read();ys=read();t=read();
	x[End]=X0,y[End++]=Y0;
	while(dis(x[0],y[0],ax*x[End-1]+bx,ay*y[End-1]+by)<=3ll*(LL)1e16)x[End]=ax*x[End-1]+bx,y[End]=ay*y[End-1]+by,End++;
	for(int i=0;i<End;i++)if(dis(xs,ys,x[i],y[i])<=t)xx[len]=x[i],yy[len++]=y[i];
	for(int i=0;i<len;i++)
	{
		LL step=t-dis(xx[i],yy[i],xs,ys),tans=1;
		int pos=i;
		while(pos+1<len && step-dis(xx[pos],yy[pos],xx[pos+1],yy[pos+1])>=0)tans++,step-=dis(xx[pos],yy[pos],xx[pos+1],yy[pos+1]),pos++;
		ans=max(ans,tans);
		step=t-dis(xx[i],yy[i],xs,ys),tans=1;pos=i;
		while(pos-1>=0 && step-dis(xx[pos],yy[pos],xx[pos-1],yy[pos-1])>=0)tans++,step-=dis(xx[pos],yy[pos],xx[pos-1],yy[pos-1]),pos--;
		ans=max(ans,tans);
	}
	cout<<ans<<endl;
	return 0;
}

E. Xenon's Attack on the Gangs

题解

好吧,看完题解之后想了半天才懂
首先,对公式有一个转化:\(S=\sum_{1\leq u,v\leq n} mex(u,v)=\sum_{1\leq x \leq n} (\sum_{mex(u,v)\geq x})\)
这个转化我就根本想不到,希望可以以后用作套路?
转化成通俗的语言就是计算大于等于\(x\) 的对数,(其中\(1\leq x \leq n\)
现在考虑怎么计算\(mex(u,v)\geq x\) 的对数。
根据\(mex\) 函数的性质,\(x\) 发挥作用的时候的前提是\(0,1,...,x-1\) 必须在之前的树链中出现, 所以\(x\) 这条边权必须插在路径由\(0,1,...,x-1\)组成树链的后面
设树链两端分别为\(u,fa[v]\),\(v\)是我们要接在\(fa[v]\) 后面的,那么这个玩意产生的贡献是:\(size[u]*size[v]\) 具体对应哪个根已经乱套了,反正就是那两边的子树相乘,这个玩意就是\(mex(u,v)\geq x\) 的对数。
然后依据这个性质,设\(dp[u][v]\) 表示把\(0,1,...,l-1\)怼在\(u,v\) 两端的答案,转移的话,就是往两端继续接新的节点,复杂度\(O(n^2)\)
代码懒得自己写了,直接蒯的别人的
好难啊,好难啊

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=3e3+10;
ll head[maxn];
ll sz[maxn][maxn];
ll f[maxn][maxn];
ll dp[maxn][maxn];
struct Edge{
    ll v,next;
}e[maxn<<1];
ll cnt=0;
void add(ll u,ll v){
    e[cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(ll u,ll fa,ll root){
    sz[root][u]=1;
    f[root][u]=fa;
    for(ll i=head[u];~i;i=e[i].next){
        ll v=e[i].v;
        if(v==fa)continue;
        dfs(v,u,root);
        sz[root][u]+=sz[root][v];
    }
}
ll solve(ll i,ll j){
    if(dp[i][j]!=-1)return dp[i][j];
    if(i==j)return dp[i][j]=0;
    return dp[i][j]=sz[i][j]*sz[j][i]+max(solve(f[i][j],i),solve(f[j][i],j));
}
int main(){
    memset(head,-1,sizeof(head));
    memset(dp,-1,sizeof(dp));
    ll n;
    scanf("%lld",&n);
    for(ll i=1;i<n;i++){
        ll u,v;
        scanf("%lld%lld",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(ll i=1;i<=n;i++){
        dfs(i,-1,i);
    }
    ll ans=0;
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++){
            ans=max(ans,solve(i,j));
        }
    }
    cout<<ans<<endl;
}

F. Chaotic V.

题解

真是太感谢钟梓皓学长耐心的把我这道题教会了!!
首先一个普适性的问题,一棵树上有若干个关键点,选取一个点使得所有关键到他的距离和最小。
这个怎么做呢?先设点在根,然后开始模拟这个点的移动。假设这个点要往以根为\(u\) 的子树上移动,对答案造成的影响是\(n-2*size[u]\) ,所以只需要找到最大的\(size[u]\) ,并判断是否\(n-2*size[u]\) 小于0即可,这么着每一次\(maxsize\) 最少减1,复杂度是\(O(n)\)
然后对每一个阶乘预处理出所有的因子就好了

#include <bits/stdc++.h>
 
#define f first
#define s second
#define ll long long
#define ull unsigned long long
#define mp make_pair
#define pb push_back
#define vi vector <int>
#define ld long double
#define pii pair<int, int>
#define y1 sda
 
using namespace std;    
const int N = 5000 + 12, mod = int(1e9)  + 7; 
 
int n,lp[N],cnt[N],c1[N], pref[N], c2[N], d[N], pr[N], pn,num[N], dist[N][N];
ll ans;
 
int sz;
vector <int> g[N], all;
 
int main () {
	scanf("%d", &n);
 
	for(int i = 1,x; i <= n; i++){
		scanf("%d", &x);
		sz = max(sz, x);
		if(x == 0) x++;
		cnt[x]++;
	}
 
	//sz = 5000;
 
	for(int i = 2; i <= sz; i++) if(!lp[i]){
		pr[++pn] = i;
		num[i] = pn;
		for(int j = i; j <= sz; j += i) lp[j] = i;
	}
	for(int i = 2,x,p; i <= sz; i++){
	    x = i;
	    d[i] += d[i - 1];
	    while(x > 1){
	    	p = lp[x];
	    	while(x % p == 0){
	    		x /= p;
	    		d[i]++;
	    	}
	    }
	}
	for(int i = 1,x,p; i <= sz; i++){
		x = i;
		while(x > 1){
			p = lp[x];
			while(x % p == 0){
				x /= p;
				c1[num[p]]++;
			}
		}
		for(int j = 1; j <= pn; j++){
			if(c1[j] > 0){
				int x = c1[j];
				while(x --> 0){
					g[i].pb(j);
				}
			}
		}
		all.pb(i);
	}
 
	ans = 0;
 
	for(int i : all){
		ans += 1ll * cnt[i] * d[i];
	}
 
	while(true){
		int res = 0, mx = 0, id = 0;
		for(int i : all){
			res += cnt[i];
			if(g[i].size() > 0){
				c2[g[i].back()] += cnt[i];	
			}
		}
		for(int j = 1; j <= pn; j++){
			if(c2[j] > mx){
				mx = c2[j];
				id = j;
			}
			c2[j] = 0;
		}
		if(2 * mx <= n){
			break;
		}
		else{
			ans += n - 2 * mx;
			vector <int> cur;
			for(int i : all){
				if(g[i].size() > 0 && g[i].back() == id){
					g[i].pop_back();
					cur.pb(i);
				}
			} 
			all = cur;
		}
	}
 
	printf("%lld", ans);
 
 
 
return 0;
}

废话

这两次cf太浪了,开局rating就掉的很惨,怕不会重新开个号
另外,现在看英文题解能力还是不行,每句话能凑合看懂,但是连起来就不知道在说啥了

posted @ 2020-01-21 14:12  小飞淙的云端  阅读(165)  评论(0编辑  收藏  举报