C. And and Pair(Dp)
要使得x&n=x,那么就只有n二进制表示为1的位能使得x在这一位为1,同时x<=n的条件也满足了。
设dp[i][0]表示到第i位,x还没过1的方案数,dp[i][1]表示到第i位x放过1的方案数。
然后根据第i位的字符进行转移即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
const int mod=1e9+7;
char s[N];
int n;
ll dp[N][2];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%s",s+1);
n=strlen(s+1);
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
if(s[i]=='1')
{
dp[i][1]=(dp[i][1]+dp[i-1][1]*3+dp[i-1][0])%mod;
}
else
{
dp[i][1]=(dp[i][1]+dp[i-1][1]*2)%mod;
}
dp[i][0]=dp[i-1][0];
}
printf("%lld\n",(dp[n][0]+dp[n][1])%mod);
}
}
E. Bob’s Problem(Kruskal最大生成树)
把黑边全部加入,白边按权值排序做一颗最大生成树即可,判断连完后是否是一整个连通块。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
typedef long long ll;
int n,m,k,f[N];
struct node
{
int u,v,w;
node(int u=0,int v=0,int w=0):u(u),v(v),w(w){
}
bool operator<(const node&o)const
{
return w>o.w;
}
};
vector<node>v[2];
int getf(int x){
return f[x]==x?x:f[x]=getf(f[x]);}
int main()
{
int t;scanf("%d",&t);
while(t--)
{
v[0].clear();v[1].clear();
scanf("%d%d%d",&n,&m,&k);
assert(m<=500000);assert(k<=500000);
assert(n<=50000);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
int x,y,w,p;scanf("%d%d%d%d",&x,&y,&w,&p);
v[p].push_back(node(x,y,w));
}
ll ans=0;
int cnt=0;
for(int i=0;i<v[0].size();i++)
{
ans+=v[0][i].w;
int fu=getf(v[0][i].u),fv=getf(v[0][i].v);
if(fu!=fv) f[fu]=fv,cnt++;
}
priority_queue<int>q;
sort(v[1].begin(),v[1].end());
for(int i=0;i<v[1].size()&&k;i++)
{
int fu=getf(v[1][i].u),fv=getf(v[1][i].v);
if(fu==fv)
{
q.push(v[1][i].w);continue;
}
cnt++;
k--;ans+=v[1][i].w;f[fu]=fv;
}
if(cnt!=n-1) {
printf("-1\n");continue;}
while(k&&!q.empty())
{
ans+=q.top();q.pop();k--;
}
printf("%lld\n",ans);
}
}
G. Eating Plan
此题看起来不好做,但是看到ai是n的一个排列,并且模数的特殊性,可以猜想到当a!大到一定程度一定会为0,否则此题根本不可解。
然后本地打表可以发现只有大概前3000个以下的数的阶乘不为0,其它的都为0,因为0对取模没有影响。
于是O(3000*3000)枚举所有左右端点求出长度为i,能获得的最大模数ki,然后把查询排序遍历一遍即可。
#include<bits/stdc++.h>
using namespace std;
const int p=2803,mod=998857459,N=1e5+5;
typedef long long ll;
int n,m,a[N],s[N],ans[N];
ll f[N],sum[N],ff[N];
vector<int>v;
struct node
{
int x,id;
bool operator<(const node&o)const
{
return x<o.x;
}
}q[N];
int main()
{
memset(ans,-1,sizeof(ans));
f[0]=f[1]=1;
for(int i=2;i<N;i++) f[i]=f[i-1]*i%mod;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]=f[a[i]];
ff[1]=max(ff[1],(ll)a[i]);
if(a[i]!=0) v.push_back(i);
sum[i]=(sum[i-1]+a[i])%mod;
}
for(int i=0;i<v.size();i++)
for(int j=i;j<v.size();j++)
{
ff[v[j]-v[i]+1]=max(ff[v[j]-v[i]+1],(sum[v[j]]-sum[v[i]-1]+mod)%mod);
}
for(int i=1;i<=m;i++)
scanf("%d",&q[i].x),q[i].id=i;
sort(q+1,q+1+m);
int j=1;
for(int i=1;i<=n;i++)
{
while(j<=m&&q[j].x<=ff[i])
ans[q[j].id]=i,j++;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
K. Tree(哈希+线段树+启发式合并)
题意:找出有序对(u,v)的个数,满足以下条件:
u!=v
u,v互相不为对方的祖先
u,v的距离<=k
lca(u,v)的权值乘2==u,
我是真的看不懂up to k是<=k的意思。。。原谅我英语水平太菜。
考虑枚举lca,给lca为u时,每次加入一个子树,给不同的权值开一个线段树的根,每棵线段树保存权值为a[x],不同深度的点的数量。
用树上启发式合并可以保证复杂度。
但是再考虑到线段树的开销问题,其实只要开n个线段树的根就可以了。
再合并的时候把点v的子树合并到lca的子树中时,如果lca中不包含点v中权值一样的根,直接把点v中的根copy进去并且树的形态不需要改变。
这里用哈希表就可以做到。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=5000011;
ll ans;
int rt[N],num,ls[N*40],rs[N*40],sum[N*40];
int n,mx,k,a[N],dep[N],f[N],tot,head[N],nex[N],to[N];
void add(int u,int v){
to[++tot]=v;nex[tot]=head[u];head[u]=tot;}
int query(int l,int r,int x,int now)
{
if(!now||l>x) return 0;
if(r<=x) return sum[now];
int m=l+r>>1;
return query(l,m,x,ls[now])+query(m+1,r,x,rs[now]);
}
void fix(int l,int r,int x,int &now)
{
if(!now) now=++num;
sum[now]++;
if(l==r) return;
int m=l+r>>1;
if(x<=m) fix(l,m,x,ls[now]);
else fix(m+1,r,x,rs[now]);
}
struct hs_table
{
int tot,num,head[M],nex[M],f[M];
ll to[M];
hs_table(){
memset(head,0,sizeof head);tot=num=0;}
int ins(ll x,int s)
{
int t=x%M;
for(int i=head[t];i;i=nex[i])
if(to[i]==x) return f[i];
to[++tot]=x;nex[tot]=head[t];head[t]=tot;
f[tot]=s==-1?++num:s;
return f[tot];
}
int query(ll x)
{
int t=x%M;
for(int i=head[t];i;i=nex[i])
if(to[i]==x) return f[i];
return -1;
}
}hs;
vector<int>vc[N];
void dfs(int u,int p)
{
dep[u]=dep[p]+1;
mx=max(mx,dep[u]);
for(int i=head[u];i;i=nex[i])
dfs(to[i],u);
}
bool vis[N];
int top,st[N];
void dfs(int u)
{
ll fu,fv;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
dfs(v);
fv=f[v];fu=f[u];
if(vc[fv].size()<vc[fu].size())
{
for(int j=0;j<vc[fv].size();j++)
{
int x=vc[fv][j],s=hs.query(fu*1000000000+2*a[u]-a[x]);;
if(dep[x]-dep[u]>=k||s==-1) continue;
ans+=query(1,mx,k-dep[x]+2*dep[u],rt[s]);
}
for(int j=0;j<vc[fv].size();j++)
{
vc[fu].push_back(vc[fv][j]);
if(vis[a[vc[fv][j]]]) continue;
int x=vc[fv][j],s=hs.query(fu*1000000000+a[x]);
if(s==-1)
hs.ins(fu*1000000000+a[x],hs.query(fv*1000000000+a[x])),vis[a[x]]=true,st[++top]=a[x];
else fix(1,mx,dep[x],rt[s]);
}
}
else
{
for(int j=0;j<vc[fu].size();j++)
{
int x=vc[fu][j],s=hs.query(fv*1000000000+2*a[u]-a[x]);;
if(dep[x]-dep[u]>=k||s==-1) continue;
ans+=query(1,mx,k-dep[x]+2*dep[u],rt[s]);
}
for(int j=0;j<vc[fu].size();j++)
{
vc[fv].push_back(vc[fu][j]);
if(vis[a[vc[fu][j]]]) continue;
int x=vc[fu][j],s=hs.query((ll)fv*1000000000+a[x]);
if(s==-1)
hs.ins(fv*1000000000+a[x],hs.query(fu*1000000000+a[x])),vis[a[x]]=true,st[++top]=a[x];
else fix(1,mx,dep[x],rt[s]);
}
f[u]=f[v];
}
for(int i=1;i<=top;i++) vis[st[i]]=false;
top=0;
}
fu=f[u];
int s=hs.ins(fu*1000000000+a[u],-1);
fix(1,mx,dep[u],rt[s]);
vc[fu].push_back(u);
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=i;
for(int i=2;i<=n;i++)
{
int x;scanf("%d",&x);
add(x,i);
}
dfs(1,0);
dfs(1);
printf("%lld\n",ans*2);
}
L. Who is the Champion
题意:有n个球队,给出n*n的矩阵第i行第j列表示第i个球队和第j个球队踢球进球的个数。两个球队pk,赢的球队加3分,输的球队不加分。平局各加一分。规定分数最多的球队为冠军,如果有多个分数最多的,按每个队赢球数-失球数最高的,如果仍有多个,输出play-offs。否则输出第几个球队是冠军。
本场的签到题,读懂题意模拟即可。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,a[N][N],b[N],c[N];
struct node
{
int x,f,s;
node(int x=0,int f=0,int s=0):x(x),f(f),s(s){
}
bool operator<(const node&o)const
{
return f==o.f?s>o.s:f>o.f;
}
}p[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) continue;
c[i]+=a[i][j];
if(a[i][j]>a[j][i]) b[i]+=3;
else if(a[i][j]==a[j][i]) b[i]++;
c[i]-=a[j][i];
}
for(int i=1;i<=n;i++)
p[i]=node(i,b[i],c[i]);
sort(p+1,p+1+n);
if(n==1) printf("1\n");
else
{
if(p[1].s==p[2].s&&p[1].f==p[2].f) printf("play-offs\n");
else printf("%d\n",p[1].x);
}
}