Contest Info##
Solved | A | B | C | D | E | F | G | H | I | J | K |
---|---|---|---|---|---|---|---|---|---|---|---|
2/12 | O | - | (varnothing) | (varnothing) | - | - | - | - | (varnothing) | O | (varnothing) |
- O 赛中通过
- (varnothing)赛后通过
- !尝试了但失败了
- -没有尝试
Soulation##
A:meeting###
题意:
给一棵树,给出(k)个节点表示一个人,求一个节点使所有人聚集在一起的时间最短。
思路:
和校赛的一道题目很像,校赛是给出若干只青蛙,然后求有多少个节点(k)步内能到达全部的青蛙。
对于这道题,可以跑这些人的直径(d),答案就是(lceil frac d2
ceil)。
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int _,n,m,d,col[maxn],dis[maxn],s,t;
vector<int> G[maxn];
void dfs(int u,int p) {
dis[u]=dis[p]+1;
for(int i=0;i<G[u].size();i++) {
int v=G[u][i];
if(v!=p) {
//printf("%d--->%d
",u,v);
dfs(v,u);
}
}
}
int dfss(int u,int t,int p,int cnt) {
if(u==t) return cnt;
for(int i=0;i<G[u].size();i++) {
int v=G[u][i];
if(v!=p) dfss(v,t,u,cnt+1);
}
}
int main() {
scanf("%d%d",&n,&d);
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);
}
int x;
for(int i=1;i<=d;i++) {
scanf("%d",&x);
col[x]=1;
}
dis[0]=1;
dfs(x,0);
int maxx;
maxx=-1,s=-1;
for(int i=1;i<=n;i++) {
if(dis[i]>maxx&&col[i]) {
maxx=dis[i];
s=i;
}
}
// puts("");
dis[0]=-1;
dfs(s,0);
maxx=-1,t=-1;
for(int i=1;i<=n;i++) {
if(dis[i]>maxx&&col[i]) {
maxx=dis[i];
t=i;
}
}
if(dis[t]&1) dis[t]+=1;
printf("%d
",dis[t]/2);
}
C:sequence###
题意:
给出(a_{1ldots n})和(b_{1ldots n}),求(max_{1{leq}l{leq}r{leq}n}{minleft(a_{l{ldots}r}
ight){ imes}sumleft(b_{lldots r}
ight) })
思路:
对于区间(lldots r),(min)可能是同一个值,我们可以用单调栈维护出每个点左右第一个比他小的。(L[i]、R[i])表示最小是(a_i)的区间,所以可以根据(min)的正负来确定后面(sum)取最大还是取最小。用线段树维护前缀和的区间最值。
如果(min > 0),(ans=a[i] ast left(query(i,R[i])_{max} - query(L[i]-1,i-1)_{min}
ight)),即(a[i])乘以右边的最大值减左边的最小值。
若(min leq 0) ,相反即可。
//#define DEBUG
#include<bits/stdc++.h>
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
const int N=3000010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
ll sum[N];
int a[N],b[N];
int L[N],R[N],st[N];
ll Max[N<<2],Min[N<<2];
void build(int l,int r,int rt) {
if(l==r) {
Max[rt]=Min[rt]=sum[l];
return ;
}
int mid=l+r>>1;
build(l,mid,lson);
build(mid+1,r,rson);
Max[rt]=max(Max[lson],Max[rson]);
Min[rt]=min(Min[lson],Min[rson]);
}
pair<ll,ll> query(int L,int R,int l,int r,int rt) {
if(L<=l&&R>=r) {
return make_pair(Max[rt],Min[rt]);
}
pair<ll,ll> res(-INF,INF);
int mid=l+r>>1;
if(L<=mid) res=query(L,R,l,mid,lson);
if(R>mid) {
auto it=query(L,R,mid+1,r,rson);
res.first=max(it.first,res.first);
res.second=min(it.second,res.second);
}
return res;
}
int main() {
#ifdef DEBUG
freopen("in.txt","r",stdin);
#endif
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) {
scanf("%lld",&b[i]);
sum[i]=sum[i-1]+b[i];
}
build(0,n,1);
int top=0;
for(int i=1;i<=n;i++) {
while(top&&a[i]<=a[st[top-1]]) top--;
if(!top) L[i]=1;
else L[i]=st[top-1]+1;
st[top++]=i;
}
top=0;
for(int i=n;i>=1;i--) {
while(top&&a[i]<=a[st[top-1]]) top--;
if(!top) R[i]=n;
else R[i]=st[top-1]-1;
st[top++]=i;
}
ll ans=-INF;
for(int i=1;i<=n;i++) {
ll tmp=0;
if(a[i]>0) {
tmp=query(i,R[i],0,n,1).first-query(L[i]-1,i-1,0,n,1).second;
} else if(a[i]<0) tmp=query(i,R[i],0,n,1).second-query(L[i]-1,i-1,0,n,1).first;
ans=max(ans,a[i]*1LL*tmp);
}
printf("%lld
",ans);
}
D:tirples I###
题意:
给出一个数(a),让你输出最少的数(x_1,x_2,ldots,x_n),使(x_1 | x_2 |{ldots}|x_n = a),其中(3|x_i)。
思路:
很明显,若(3|a),则输出(a)即可。否则考虑(a)的二进制位有几个,若(a)的二进制位小于等于(2)无解。
然后讨论
若(a mod 3 =1)
- 如果(a)中的二进制位至少有两个(mod 3= 1),设它们为(p、q),我们可以取({a-p,a-q})即可。
(sample):(a=13),可以取(13-1=12、13-4=9),减去(p),减去(q),后者里面包含(p),补回来了。 - 如果(a)中既有(mod 3=1)又有(mod 3=2),设(mod 3=1)为(p),(mod 3=2)为(q),我们可以取({a-p,p+q})
(sample):(a=13),可以取(13-1=12、8+1=9),减去(p),加上(p+q),补回来了。 - 如果(a)中没有(mod 3=1),设他们为(p、q、r),我们可以取({a-p-q,p+q+r})
(sample):(difficulty ldots),同理。
若(a mod 3=2)
相反即可。
//#define DEBUG
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
vector<ll> b[2];
int main() {
#ifdef DEBUG
freopen("in.txt","r",stdin);
#endif
int _;ll a;
for(scanf("%d",&_);_;_--) {
memset(b,0,sizeof(b));
scanf("%lld",&a);
if(a%3==0) {
printf("1 %lld
",a);
continue;
}
for(int i=0;i<=60;i++) {
if((a>>i)&1) b[i&1].push_back(1LL<<i);
}
if(a%3==1) {
if(b[0].size()>=2) {
printf("2 %lld %lld
",a-b[0][0],a-b[0][1]);
} else if(b[0].size()&&b[1].size()) {
printf("2 %lld %lld
",a-b[0][0],b[0][0]+b[1][0]);
} else printf("2 %lld %lld
",a-b[1][0]-b[1][1],b[1][0]+b[1][1]+b[1][2]);
} else {
if(b[1].size()>=2) {
printf("2 %lld %lld
",a-b[1][0],a-b[1][1]);
} else if(b[0].size()&&b[1].size()) {
printf("2 %lld %lld
",a-b[1][0],b[1][0]+b[0][0]);
} else printf("2 %lld %lld
",a-b[0][0]-b[0][1],b[0][0]+b[0][1]+b[0][2]);
}
}
}
I:string###
题意:
给出一个字符串(S),求出(S)的一个字串集合,使得集合中的任何一个串反转后,不会出现在集合中。
思路:
我们可以构建(S#revleft(S
ight)),然后用后缀数组处理出有多少个不用的子串,减去包含(#)的字串,发现除了回文串,其他的都是对称出现的,共(p)个,所以可以用回文树求出本质不同的回文串个数(q)。所以答案就是(frac{left(p+q
ight)}2)。
解释:设字符串(S)的长度为(len),包含(#)的字串个数就是({left(len+1
ight)}^2),画图就看出来了。从第一个到(#),每个都有(left(len+1
ight))个字串。后缀数组处理不同的子串就是(sum_{i=2}^{2{ast}len+1}left(2{ast}len+1-sa[i]-h[i]
ight))。见下图
红色表示(h[])数组,蓝色表示首字母开始的下标,黑色字串为排序后的排列。
总长度减去蓝色下标——表示该位置开始能形成的子串个数,绿色是减去(h[i])的结果,即贡献。
累加贡献就是不同字串个数了。
取自(csl)巨巨的代码,不知道理解的对不对,见注释。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1 << 19;
struct Suffix_Array
{
char s[maxn];
int sa[maxn], t[maxn], t2[maxn], c[maxn], rank[maxn], height[maxn];
void build_sa(int m, int n)
{
n++;
int *x = t, *y = t2;
for (int i = 0; i < m; i++) c[i] = 0;
for (int i = 0; i < n; i++) c[x[i] = s[i]]++;
for (int i = 1; i < m; i++) c[i] += c[i - 1];
for (int i = n - 1; ~i; i--) sa[--c[x[i]]] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
for (int i = n - k; i < n; i++) y[p++] = i;
for (int i = 0; i < n; i++)
if (sa[i] >= k) y[p++] = sa[i] - k;
for (int i = 0; i < m; i++) c[i] = 0;
for (int i = 0; i < n; i++) c[x[y[i]]]++;
for (int i = 1; i < m; i++) c[i] += c[i - 1];
for (int i = n - 1; ~i; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (int i = 1; i < n; i++)
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
if (p >= n) break;
m = p;
}
n--;
int k = 0;
for (int i = 0; i <= n; i++) rank[sa[i]] = i;
for (int i = 0; i < n; i++)
{
if (k) k--;
int j = sa[rank[i] - 1];
while (s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
}
int dp[maxn][30];
void initrmq(int n)
{
for (int i = 1; i <= n; i++)
dp[i][0] = height[i];
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
}
int rmq(int l, int r)
{
int k = 31 - __builtin_clz(r - l + 1);
return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}
int lcp(int a, int b)
{
a = rank[a], b = rank[b];
if (a > b) swap(a, b);
return rmq(a + 1, b);
}
//
long long solve(int n, int sz) // ans= sum{sz-sa[i]+1-h[i]} 见图解
{ //但是算带'$'的时候不带末尾'0',所以这里也不带。sz-sa[i]+h[i]
long long ans = 1LL * sz * (sz + 1) / 2; // sa[0]=sz,所以sz-sa[i]累加就是 1+2+……+sz
for (int i = 1; i <= sz; i++) ans -= height[i];
ans -= 1LL * n * n + 2LL * n + 1; // (len+1)^2
return ans;
}
} sa;
struct Palindromic_Tree
{
int ch[maxn][26], f[maxn], len[maxn], s[maxn];
int cnt[maxn]; // 结点表示的本质不同的回文串的个数(调用count()后)
int num[maxn]; // 结点表示的最长回文串的最右端点为回文串结尾的回文串个数
int last, sz, n;
int newnode(int x)
{
memset(ch[sz], 0, sizeof(ch[sz]));
cnt[sz] = num[sz] = 0, len[sz] = x;
return sz++;
}
void init()
{
sz = 0;
newnode(0), newnode(-1);
last = n = 0, s[0] = -1, f[0] = 1;
}
int get_fail(int u)
{
while (s[n - len[u] - 1] != s[n]) u = f[u];
return u;
}
void add(int c)
{ // c-='a'
s[++n] = c;
int u = get_fail(last);
if (!ch[u][c])
{
int np = newnode(len[u] + 2);
f[np] = ch[get_fail(f[u])][c];
num[np] = num[f[np]] + 1;
ch[u][c] = np;
}
last = ch[u][c];
cnt[last]++;
}
void count()
{
for (int i = sz - 1; ~i; i--) cnt[f[i]] += cnt[i];
}
} pam;
int main()
{
scanf("%s", sa.s);
pam.init();
int n = strlen(sa.s);
for (int i = 0; sa.s[i]; i++) pam.add(sa.s[i] - 'a');
sa.s[n] = '$';
for (int i = 0, j = n - 1; i < n; i++, j--) sa.s[n + 1 + i] = sa.s[j];
sa.s[n * 2 + 1] = ' ';
sa.build_sa(128, n * 2 + 1);
long long p = sa.solve(n, n * 2 + 1);
long long q = pam.sz-2; //节点大小即为本质不同回文子串个数
printf("%lld
", (p + q) / 2);
// for(int i=0;i<=n*2+1;i++) printf("%c**",sa.s[i]);
// puts("");
// for(int i=0;i<=n*2+1;i++) {
// printf("%d*****",sa.height[i]);
// for(int j=sa.sa[i];j<=n*2+1;j++) printf("%c**",sa.s[j]);
// puts("");
// }
// for(int i=0;i<=n*2+1;i++) printf("%d**",sa.sa[i]);
// printf("%d&&&",q);
}
K:number###
题意:
给出一个字符串,问有多少个不同的子串能整除(300)。子串不同即起始位置不同。
思路:
一道去年暑假队友做到过的原题。虽然是赛后我才回忆起来的。前缀和或(DP)
能整除(300),即数位和是(3)的倍数且末尾至少两个(0)(0除外)
所以可以求前缀和(mod 3),若要和为(3)的倍数,(sum[r]-sum[l]=0) ,所以我们可以同时记录前面有多少个(0,1,2)。当连续两个(0)的时候就算一次。
//#define DEBUG
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
char s[N];
int w[N],cnt[3];
int main() {
#ifdef DEBUG
freopen("in.txt","r",stdin);
#endif
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++) {
w[i]=(w[i-1]+s[i]-'0')%3;
}
ll ans=0;
for(int i=1;i<=len;i++) {
if(s[i]=='0') {
ans++;
if(s[i-1]=='0') {
ans+=cnt[w[i]];
}
}
cnt[w[i-1]]++;
}
printf("%lld
",ans);
}
(dp[i][j])表示以第i个字符结尾的子串中数位和(mod 3=j)的个数。(0)另外统计。每次遇到两个连续的(0),算一次答案。
//#define DEBUG
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
char s[N];
int dp[N][3];
int main() {
#ifdef DEBUG
freopen("in.txt","r",stdin);
#endif
scanf("%s",s+1);
int len=strlen(s+1);
ll ans=0,res=0;
for(int i=1;i<=len;i++) {
int num=(s[i]-'0')%3;
dp[i][num]++;
for(int j=0;j<3;j++) {
dp[i][j]+=dp[i-1][(j-num+3)%3];
}
if(s[i]=='0') res++;
if(i>=2&&s[i]=='0'&&s[i-1]=='0') ans+=dp[i-1][0];
}
printf("%lld
",ans+res);
}
J:free###
题意:
给出一个无向连通图,每条边有相应的花费,然后可以选择最多(k)条边免费,求(s-t)的最小花费。
思路:
飞行路线原题,(dp[i][j])表示到达(i)点用了(j)次特权的花费。可能不足(k)条边,所以选最小值。(spfa)搞一搞。
//#define DEBUG
#include<bits/stdc++.h>
using namespace std;
const long long inf=92233720368547758;
const int maxn=2010;
const int maxm=2010;
typedef long long ll;
int n,m,k,st,ed;
int head[maxn];
ll dis[maxn][1010];
bool vis[maxn][1010];
struct Edge{
int to,next;
ll w;
}edge[100010];
int tol;
void addedge(int u,int v,ll w) {
edge[tol].to=v;
edge[tol].w=w;
edge[tol].next=head[u];
head[u]=tol++;
}
void init() {
tol=0;
for(int i=0;i<=n;i++) {
head[i]=-1;
for(int j=0;j<=1010;j++) {
dis[i][j]=inf;
vis[i][j]=false;
}
}
}
struct node{
int u,step;
};
void spfa() {
dis[st][0]=0;
queue<node> q;
vis[st][0]=true;
q.push({st,0});
while(!q.empty()) {
node f=q.front();
q.pop();
vis[f.u][f.step]=false;
for(int i=head[f.u];i!=-1;i=edge[i].next) {
int v=edge[i].to;
if(dis[v][f.step]>dis[f.u][f.step]+edge[i].w) {
dis[v][f.step]=dis[f.u][f.step]+edge[i].w;
if(!vis[v][f.step]) {
vis[v][f.step]=true;
q.push({v,f.step});
}
}
if(f.step+1<=k) {
if(dis[v][f.step+1]>dis[f.u][f.step]) {
dis[v][f.step+1]=dis[f.u][f.step];
if(!vis[v][f.step+1]) {
vis[v][f.step+1]=true;
q.push({v,f.step+1});
}
}
}
}
}
}
int main() {
#ifdef DEBUG
freopen("in.txt","r",stdin);
#endif // DEBUG
while(~scanf("%d%d%d%d%d",&n,&m,&st,&ed,&k)) {
init();
for(int i=1;i<=m;i++) {
int u,v;
ll w;
scanf("%d%d%lld",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
spfa();
ll ans=inf;
for(int i=0;i<=k;i++) ans=min(ans,dis[ed][i]);
printf("%lld
",ans);
}
}