GYM104081 部分题解
比赛链接:https://codeforces.com/gym/104081
目前就做了 8 题,里面还有 4 个水题……
水题:ACEG,模拟题意即可,C 和 E 有一些细节。不想写题解了
F
首先目标是如何将这 9 个数分组,由于答案一定存在,考虑随机化,固定 ,然后随机一个 ,异或得到 的另一个元素,同理第二个集合只需要随机两个。作为可以证明期望 20 次内就可以完成分组。
完成分组之后如何确定 ?首先每组最大的一定是 ,但是 xor 和 and 的大小不好确定,直接 暴力枚举 xor/and。
不妨看每一个二进制位,如果 or 为 0,那么肯定两个数该位都是 0,and 为 1,则都为 1. 统计答案即可
代码:
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
int rnd(int l,int r){return rnd(r-l+1) + l;}
ll a[15],ansp,ansq,ansr;
int ck(vector<ll>d[3]){
// a|b b|c c|a
// a&b b&c c&a
ll p,q,r;
p=q=r=0;
for(int i=60;i>=0;i--){
vector<int>conf(3,-1);
for(int j=0;j<3;j++){
if(((d[j][0] >> i)&1) == 0)conf[j] = conf[(j+1)%3] = 0;
if(((d[j][1] >> i)&1) == 1)conf[j] = conf[(j+1)%3] = 1;
}
for(int j=0;j<3;j++)
if(conf[j] == -1)conf[j] = conf[(j+1)%3] ^ 1;
if(conf[0]==1)p |= 1ll<<i;
if(conf[1]==1)q |= 1ll<<i;
if(conf[2]==1)r |= 1ll<<i;
}
if((p|q)==d[0][0]&&(p&q)==d[0][1]&&(q|r)==d[1][0]&&(q&r)==d[1][1]&&(r|p)==d[2][0]&&(r&p)==d[2][1]){
ansp=p,ansq=q,ansr=r;
return 1;
}
return 0;
}
void solve(){
map<ll,int>is;
for(int i=1;i<=9;i++)scanf("%lld",&a[i]),is[a[i]]++;
while(1){
int a02,b01,b02;
ll a2,b1=-1,b2=-1;
a02=rnd(2,9);
a2 = a[a02];
do{b01=rnd(2,9);}while(b01 == a02);
do{b02=rnd(2,9);}while(b02 == a02 || b02 == b01);
b1=a[b01],b2=a[b02];
map<ll,int>is2;
is2 = is;
-- is2[b1], -- is2[a[1]], -- is2[a2], -- is2[b2];
if(-- is2[b1^b2] < 0){
continue;
}
if(-- is2[a[1]^a2] < 0){
continue;
}
vector<ll>c[3],d[3];
c[0].pb(a[1]),c[0].pb(a2),c[0].pb(a[1]^a2);
c[1].pb(b1),c[1].pb(b2),c[1].pb(b1^b2);
for(auto &u : is2)while(u.second){
c[2].pb(u.first);
-- u.second;
}
for(int i=0;i<3;i++)sort(c[i].begin(),c[i].end(),greater<ll>());
for(int i=0;i<=1;i++){
d[0]=c[0];
if(i==1)swap(d[0][1],d[0][2]);
for(int j=0;j<=1;j++){
d[1]=c[1];
if(j==1)swap(d[1][1],d[1][2]);
for(int k=0;k<=1;k++){
d[2]=c[2];
if(k==1)swap(d[2][1],d[2][2]);
int t=ck(d);
if(t==1){
printf("%lld %lld %lld\n",ansp,ansq,ansr);
return ;
}
}
}
}
}
}
signed main(){
int te;scanf("%d",&te);
while(te --)solve();
return 0;
}
H
带步数限制的 dijkstra ,每次多记一个"步数",其它完全一致
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 505;
int n,m;
int a[505][505];
vector<pii>g[505];
int ed,vis[maxn][maxn];
ll w[505];
ll dis[maxn][maxn];
struct node{
int cs;
ll dis;
int now;
};
bool operator < (node a,node b){
if(a.cs != b.cs)return a.cs > b.cs;
return a.dis>b.dis;
}
void dijkstra(){
memset(dis,0x3f,sizeof dis);
priority_queue<node>pq;
pq.push(node{0,0,1});
dis[1][0] = 0;
while(!pq.empty()){
node now = pq.top();pq.pop();
int u = now.now, cs =now.cs;
if(vis[u][cs])continue;
vis[u][cs] = 1;
for(auto v : g[u]){
if(cs+1 <= n-1 && dis[u][cs] + v.second < dis[v.first][cs+1]){
dis[v.first][cs + 1] = dis[u][cs] + v.second;
pq.push(node{cs+1, dis[v.first][cs+1], v.first});
}
}
}
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
g[x].pb(mpr(y,z));
g[y].pb(mpr(x,z));
}
dijkstra();
int qu;scanf("%d",&qu);
for(int i=1;i<=qu;i++){
scanf("%d",&ed);
for(int j=1;j<=n-1;j++){
scanf("%lld",&w[j]);
}
for(int j=2;j<=n-1;j++)w[j] += w[j-1];
ll ans = 1e18;
for(int j=1;j<=n-1;j++)
ans = min(ans, dis[ed][j] + w[j]);
printf("%lld\n",ans);
}
return 0;
}
I
一眼 dp,设 表示当前考虑到字符串第 位,上一个字符串是在 a/b 集合时的最优解。
转移枚举位置,用 hash/trie 判断这段字符串是否在 b/a 集合里即可。
我写了 trie,不过 hash 更简单。
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 5e5+5;
int n,m;
char s[maxn],t[maxn];
struct trie{
int tr[maxn][26];
int vis[maxn * 5];
int cnt=0;
void insert(char *t){
int len = strlen(t+1);
int now = 0;
for(int i=1;i<=len;i++){
if(!tr[now][t[i] - 'a']){
tr[now][t[i] - 'a'] = ++cnt;
}
now = tr[now][t[i] - 'a'];
}
vis[now] = len;
}
}tr[2];
vector<int> query(char *t,int id,int bs){
int nt=strlen(t+1);
int now = 0;
vector<int>vc;
for(int i=id;i<=nt;i++){
if(!tr[bs].tr[now][t[i] - 'a'])return vc;
now = tr[bs].tr[now][t[i] - 'a'];
if(tr[bs].vis[now]){
vc.pb(tr[bs].vis[now]);
}
}
return vc;
}
int dp[maxn][2];
signed main(){
memset(dp,0x3f,sizeof dp);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s + 1);
tr[0].insert(s);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%s",s + 1);
tr[1].insert(s);
}
scanf("%s",t+1);
int nt=strlen(t+1);
dp[0][0] = dp[0][1] = 0;
for(int i=1;i<=nt;i++){
vector<int>v = query(t,i,1);
// printf("! %d %d\n",i,v.size());
// if(v.size())printf("?? %d\n",v[0]);
for(int u : v)
dp[i+u-1][1] = min(dp[i+u-1][1], dp[i-1][0] + 1);
v = query(t,i,0);
for(int u : v)
dp[i+u-1][0] = min(dp[i+u-1][0], dp[i-1][1] + 1);
}
// printf("? %d\n",dp[4][0]);
if(dp[nt][0] == INF && dp[nt][1] == INF)puts("-1");
else printf("%d\n",min(dp[nt][0], dp[nt][1]));
return 0;
}
L
记一下每个结点的 级祖先,可以用倍增实现。反过来我们就得到了每个点子树内所有距离为 的点。
一个点合并子树的时候,可以使用启发式合并的 trick,即永远是小的集合 合并到大的集合中。然后删除掉所有距离为 的点的信息。
对于每个点,由于最多合并了 次,因此总时间复杂度
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;
int n,k;
int c[maxn],ans[maxn];
vector<int>g[maxn];
map<int,int>res[maxn];
vector<int>del[maxn];
int fa[maxn][21],dep[maxn];
vector<int>qw;
void dfs(int x,int fat=0){
fa[x][0] = fat;
dep[x] = dep[fat] + 1;
for(int u : g[x])if(u!=fat){
dfs(u,x);
}
}
void dfs2(int x,int fat=0){
for(int u : g[x])if(u!=fat){
dfs2(u,x);
}
if(dep[x] > k){
int v = x;
for(int j : qw)
if(k & (1<<j))v = fa[v][j];
del[v].pb(x);
}
}
void dfs3(int x,int fat=0){
res[x][c[x]] ++;
for(int u : g[x])if(u!=fat){
dfs3(u,x);
if(res[x].size() < res[u].size())swap(res[x], res[u]);
for(auto v : res[u])res[x][v.first] += v.second;
res[u].clear();
}
ans[x] = res[x].size();
for(int v : del[x]){
res[x][c[v]] --;
if(res[x][c[v]] == 0)res[x].erase(c[v]);
}
}
signed main(){
scanf("%d%d",&n,&k);
for(int i=19;i>=0;i--)
if(k&(1<<i))qw.pb(i);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
g[x].pb(y), g[y].pb(x);
}
dfs(1);
for(int j=1;j<=19;j++)
for(int i=1;i<=n;i++)
fa[i][j] = fa[fa[i][j-1]][j-1];
dfs2(1);
dfs3(1);
int qu;scanf("%d",&qu);
while(qu--){int x;scanf("%d",&x);printf("%d\n",ans[x]);}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示