C-Chino with Queue
题意是说给出了一个矩阵,矩阵W[i][j]表示j排在i之前的愉悦度。问如何排列最后得到的愉悦值是最大的。注意到最后的结果只与这一位置是谁而和放的前后顺序无关。考虑dp[S][i]表示当前状态是S,末尾是i的最大值。初始条件有dp[1<<(i-1)][i]=W[i][i],转移就把k放在最后考虑去转移就好了。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=25;
ll w[N][N];
ll dp[1<<19][19];
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lld",&w[i][j]);
for(int i=1;i<=n;i++) dp[1<<(i-1)][i]=w[i][i];
for(int i=1;i<(1<<n);i++) {
for(int j=1;j<=n;j++) {
if(i&1<<(j-1)) {
for(int k=1;k<=n;k++) {
if(!(i&1<<(k-1))) {
int m=i|(1<<(k-1));
dp[m][k]=max(dp[m][k],dp[i][j]+w[k][j]);
}
}
}
}
}
ll ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dp[(1<<n)-1][i]);
printf("%lld\n",ans);
return 0;
}
E-Chino with Triangle
给定一颗树,要问从这个树上任意选择三个点,使得这三个点的距离能够成三角形的比例是多少。给出结论:三个点能够构成三角形的充分必要条件是这个三个点不共线。如果三点共线,显然一条边是另外两条边之和,不能构成三角形。否则如果不共线的话,不妨设三个点到中心的一个点的距离分别为a,b,c。那么三边距离就分别是a+b,b+c,a+c,必定可以构成三角形。那么这里从反面考虑有多少个点对是共线的。那么不重复地统计就是对于每个结点,另外两个结点分别在两个不同的子树上面的总方案数就可以了。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e5+50;
vector<int> G[N];
int sz[N];
double ans=0.0;
int n;
void dfs(int u,int fa) {
for(auto &v:G[u]) {
if(v==fa) continue;
dfs(v,u);
ans+=1.0*sz[v]*sz[u];
sz[u]+=sz[v];
}
ans+=1.0*sz[u]*(n-1-sz[u]);
sz[u]++;
}
int main() {
scanf("%d",&n);
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
double tot=1.0*n*(n-1)*(n-2)/6;
printf("%lf\n",(tot-ans)/tot);
return 0;
}
G-Chino with Train to the Rabbit Town
给出一个序列,要求最多能够有多少个区间异或和是k。注意到任何一段连续区间的异或和都是两个前缀的异或,也就是S[i] ^ S[j]要为k,转化一下就变成S[i] ^ k=S[j]。不妨对于每个前缀都用一个set保存下来,当有后来的前缀满足条件的话就情况set并计数就好了。显然这个贪心是正确的。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=5e5+50;
int a[N];
int main() {
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]^=a[i-1];
set<int> s;
s.insert(0);
int cnt=0;
for(int i=1;i<=n;i++) {
if(s.find(a[i]^k)!=s.end()) {
++cnt;
s.clear();
}
s.insert(a[i]);
}
printf("%d\n",cnt);
return 0;
}
I-Chino with Rewrite
给定了一些点,然后给点之间连边,维护一个树的形态。要支持两个操作:1.合并两个点,把它们的权值变成平均值。2.查询两点之前有多少个不同的权值。先离线并查集建树,然后考虑树剖。操作1本质上就是修改了两个点的权值,操作2本质上就是查询所有的权值相或之后有多少个1。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e5+50,M=1e6+50;
int n,q,tim=0;
int opt[N<<2],x[N<<2],y[N<<2],sz[N],w[N],fa[N],dep[N],F[N],L[N],R[N],son[N],id[N],rk[N],bel[N];
ll sum[N<<2];
int find(int x) { return fa[x]==x?fa[x]:fa[x]=find(fa[x]); }
vector<int> G[N];
void dfs1(int u,int fa) {
dep[u]=dep[fa]+1,son[u]=-1,sz[u]=1,F[u]=fa;
for(auto &v:G[u]) {
if(v==fa) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(son[u]=-1||sz[son[u]]<sz[v]) son[u]=v;
}
}
void dfs2(int u,int top) {
bel[u]=top,L[u]=R[u]=id[u]=++tim,rk[id[u]]=u;
if(son[u]==-1) return;
dfs2(son[u],top);
for(auto &v:G[u]) {
if(v==F[u]||v==son[u]) continue;
dfs2(v,v);
}
R[u]=tim;
}
void update(int rt) { sum[rt]=sum[rt<<1]|sum[rt<<1|1]; }
void build(int rt,int l,int r) {
if(l==r) { sum[rt]=1LL<<w[rk[l]]; return; }
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
update(rt);
}
void modify(int rt,int l,int r,int x,int val) {
if(l==r) { sum[rt]=1LL<<val; return;}
int mid=(l+r)>>1;
if(x<=mid) modify(rt<<1,l,mid,x,val);
else modify(rt<<1|1,mid+1,r,x,val);
update(rt);
}
ll query(int rt,int l,int r,int L,int R) {
if(R<l||L>r) return 0;
if(L<=l&&r<=R) return sum[rt];
int mid=(l+r)>>1;
return query(rt<<1,l,mid,L,R)|query(rt<<1|1,mid+1,r,L,R);
}
ll cal(int x,int y) {
ll res=0;
while(bel[x]!=bel[y]) {
if(dep[bel[x]]<dep[bel[y]]) swap(x,y);
res|=query(1,1,n,id[bel[x]],id[x]);
x=F[bel[x]];
}
if(id[x]>id[y]) swap(x,y);
res|=query(1,1,n,id[x],id[y]);
return res;
}
int main() {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=q;i++) {
scanf("%d%d%d",&opt[i],&x[i],&y[i]);
if(opt[i]==1) {
int u=find(x[i]),v=find(y[i]);
if(u==v) continue;
fa[v]=u;
G[x[i]].push_back(y[i]);
G[y[i]].push_back(x[i]);
}
}
dfs1(1,0);dfs2(1,1);
for(int i=1;i<=n;i++) fa[i]=i;
build(1,1,n);
for(int i=1;i<=q;i++) {
if(opt[i]==1) {
int u=find(x[i]),v=find(y[i]);
if(u==v) continue;
fa[v]=u;
int mid=(w[x[i]]+w[y[i]])>>1;
modify(1,1,n,id[x[i]],mid);
modify(1,1,n,id[y[i]],mid);
w[x[i]]=w[y[i]]=mid;
}
else {
int u=find(x[i]),v=find(y[i]);
if(u!=v) puts("-1");
else printf("%d\n",__builtin_popcountll(cal(x[i],y[i])));
}
}
return 0;
}