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;
}
D. Aroma's Search
题解
发现系数最少就是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就掉的很惨,怕不会重新开个号
另外,现在看英文题解能力还是不行,每句话能凑合看懂,但是连起来就不知道在说啥了