想法
题意简述
有
m
个想法
每个题目可以表示为想法的集合。
前
m
道题目中,第
给出剩余
n−m
道题目是由哪两道题目拼成的,它的想法集合是拼成它的题目的并集。
求每道题目的想法集合的大小
数据范围
1≤m≤105
1≤n≤106
评分方法
对于每个输出文件
如果其中你有
95%
以上的行的答案和正确答案的误差不超过
25%
,那么你就可以得到分数。
所谓误差不超过
25%
,即,如果正确答案是
X
,那么你的答案在
思路
好神的题目。
对每个题目随机rand一个值。
拓扑排序,DP出每个集合前
t
小的数。
每个点满足等式
RANDMAX表示rand的值域上界。
t
越大正确的概率就越大。
为什么是对的呢?
我YY了一下。
随机值期望是平均分布的,每隔
如果是这样的话就显然了恩……
这样就可以A了。
代码
#include<cstdio>
#include<cstring>
#include<vector>
#include<cstdlib>
using namespace std;
#define N 1000010
#define lim 20
int n,m;
int ch[N][2];
int val[N];
vector<int> vec[N];
void merge(int node)
{
int sa=vec[ch[node][0]].size();
int sb=vec[ch[node][1]].size();
int nowa=0,nowb=0;
for (int i=1;i<=lim&&(nowa<sa||nowb<sb);i++)
{
if (nowa==sa)
vec[node].push_back(vec[ch[node][1]][nowb++]);
else if (nowb==sb)
vec[node].push_back(vec[ch[node][0]][nowa++]);
else if (vec[ch[node][0]][nowa]<vec[ch[node][1]][nowb])
vec[node].push_back(vec[ch[node][0]][nowa++]);
else if (vec[ch[node][0]][nowa]>vec[ch[node][1]][nowb])
vec[node].push_back(vec[ch[node][1]][nowb++]);
else
{
vec[node].push_back(vec[ch[node][0]][nowa++]);
nowb++;
}
}
}
int main()
{
srand(20000428);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
val[i]=rand()+1;
for (int i=m+1;i<=n;i++)
scanf("%d%d",&ch[i][0],&ch[i][1]);
for (int i=1;i<=m;i++)
vec[i].push_back(val[i]);
for (int i=m+1;i<=n;i++)
merge(i);
for (int i=m+1;i<=n;i++)
if (vec[i].size()<lim)
printf("%d\n",vec[i].size());
else
printf("%d\n",(int)(1.0*lim*RAND_MAX/vec[i][lim-1]));
return 0;
}
大融合
题意简述
一开始有
n
个孤立的点。
后面有
操作有两种类型:
1.将不连通的两个点连接起来。
2.问两个点之间的边(保证存在)有多少点对的路径经过。
数据范围
1≤n,q≤105
思路
离线+树链剖分+线段树+并查集。
先离线,找出最终形态的森林。
我们可以发现,每个询问一定是父亲和儿子之间的边。
那么答案就是
sizeson∗(sizeblock−sizeson)
动态维护每个点的size。
每次我们加边的影响的点,即为父亲一直沿存在的边往上的所有点。
可以用并查集找到最上面的点。
这样就变成了区间加,可以用树链剖分+线段树实现。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define N 100010
#define Q 100010
struct edge{
int s,t,next;
}e[N<<1];
int head[N],cnt;
void addedge(int s,int t)
{
e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
struct modi{
int s,t;
bool ty;
}mo[Q];
int dfn[N],size[N],deep[N],son[N],fa[N],seq[N],top[N];
bool vis[N];
int n,q,u,v,order;
namespace dsu
{
int fa[N];
void Init()
{
for (int i=1;i<=n;i++)
fa[i]=i;
}
int getfa(int x)
{
if (fa[x]!=x)
fa[x]=getfa(fa[x]);
return fa[x];
}
void unionn(int x,int y)
{
fa[x]=y;
}
}
namespace Segtree
{
struct Node{
int val,add;
Node()
{
val=add=0;
}
void modify_add(int x)
{
val+=x;
add+=x;
}
}tree[N<<2];
void build(int l,int r,int node)
{
if (l==r)
{
tree[node].val=seq[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,node<<1);
build(mid+1,r,node<<1|1);
}
void pushdown(int node)
{
if (tree[node].add)
{
tree[node<<1].modify_add(tree[node].add);
tree[node<<1|1].modify_add(tree[node].add);
tree[node].add=0;
}
}
void modify_add(int L,int R,int l,int r,int node,int val)
{
if (L<=l&&r<=R)
{
tree[node].modify_add(val);
return;
}
pushdown(node);
int mid=(l+r)>>1;
if (L<=mid)
modify_add(L,R,l,mid,node<<1,val);
if (R>mid)
modify_add(L,R,mid+1,r,node<<1|1,val);
}
void solve_add(int u,int v,int val)
{
int tu=top[u],tv=top[v];
while (tu!=tv)
{
if (deep[tu]<deep[tv])
swap(tu,tv),swap(u,v);
modify_add(dfn[tu],dfn[u],1,n,1,val);
u=fa[tu];
tu=top[u];
}
if (deep[u]>deep[v])
swap(u,v);
modify_add(dfn[u],dfn[v],1,n,1,val);
}
int query(int pos,int l,int r,int node)
{
if (l==r)
return tree[node].val;
pushdown(node);
int mid=(l+r)>>1;
if (pos<=mid)
return query(pos,l,mid,node<<1);
else
return query(pos,mid+1,r,node<<1|1);
}
}
bool read()
{
char ch=getchar();
while (ch!='A'&&ch!='Q')
ch=getchar();
return ch=='Q';
}
void dfs(int node,int lastfa,int de)
{
vis[node]=1;
size[node]=1;
deep[node]=de;
son[node]=0;
fa[node]=lastfa;
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
{
dfs(e[i].t,node,de+1);
size[node]+=size[e[i].t];
if (size[e[i].t]>size[son[node]])
son[node]=e[i].t;
}
}
void dfs2(int node,int tp)
{
dfn[node]=++order;
seq[order]=1;
top[node]=tp;
if (son[node])
dfs2(son[node],tp);
for (int i=head[node];i!=-1;i=e[i].next)
if (!dfn[e[i].t])
dfs2(e[i].t,e[i].t);
}
int main()
{
scanf("%d%d",&n,&q);
memset(head,0xff,sizeof(head));
cnt=0;
for (int i=1;i<=q;i++)
{
mo[i].ty=read();
scanf("%d%d",&mo[i].s,&mo[i].t);
if (!mo[i].ty)
addedge(mo[i].s,mo[i].t);
}
for (int i=1;i<=n;i++)
if (!vis[i])
dfs(i,i,1);
for (int i=1;i<=n;i++)
if (!dfn[i])
dfs2(i,i);
Segtree::build(1,n,1);
dsu::Init();
for (int i=1;i<=q;i++)
{
if (mo[i].ty)
{
u=mo[i].s,v=mo[i].t;
if (deep[u]<deep[v])
swap(u,v);
int x=Segtree::query(dfn[dsu::getfa(v)],1,n,1);
int y=Segtree::query(dfn[u],1,n,1);
printf("%lld\n",1LL*(x-y)*y);
}
else
{
u=mo[i].s,v=mo[i].t;
if (deep[u]<deep[v])
swap(u,v);
dsu::unionn(u,v);
Segtree::solve_add(dsu::getfa(v),v,Segtree::query(dfn[u],1,n,1));
}
}
return 0;
}
路径
题意简述
n
个点的无向图,每个点有()+-*0123456789
之一的字符。
有
你要在这张图上走恰好
k
个节点(可以走重复的点和边)
使得形成的表达式合法。
合法的条件:
1.括号配对但不能有空括号
2.加法不能做正号,但减号可以当符号
3.0可以做除数
4.数字不能有多余的前导0
求方案数模
数据范围
1≤n≤20
1≤m≤n(n−1)/2
1≤k≤30
思路
多维DP。
f[a][b][c][d]
表示走了
a
次,当前在
转移注意细节。
代码
#include<cstdio>
#include<cstring>
using namespace std;
#define N 25
const int p=1000000007;
struct edge{
int s,t,next;
}e[N*N];
int head[N],cnt;
void addedge(int s,int t)
{
e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
long long f[35][N][35][2];//num,where,(,head0
long long ans;
int n,m,k,u,v;
char st[N];
bool check(char a,char b,bool zero1,bool zero2)
{
if (zero1)
if ((b==')')||(b=='+')||(b=='-')||(b=='*')||(b=='/'))
return true;
else
return false;
else if (zero2)
if ((a=='(')||(a=='+')||(a=='-')||(a=='*')||(a=='/'))
return true;
else
return false;
else if ((a=='(')&&((b>='1'&&b<='9')||(b=='-')||b=='('))
return true;
else if ((a==')')&&((b=='+')||(b=='-')||(b=='*')||(b=='/')||(b==')')))
return true;
else if ((a>='0'&&a<='9')&&((b=='+')||(b=='-')||(b=='*')||(b=='/')||(b==')')||(b>='0'&&b<='9')))
return true;
else if ((a=='+'||(a=='-')||(a=='*')||(a=='/'))&&((b>='1'&&b<='9')||(b=='(')))
return true;
else
return false;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
scanf("%s",st+1);
memset(head,0xff,sizeof(head));
cnt=0;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
}
for (int i=1;i<=n;i++)
if (st[i]=='(')
f[1][i][1][0]=1;
else if (st[i]=='0')
f[1][i][0][1]=1;
else if (st[i]=='-')
f[1][i][0][0]=1;
else if (st[i]>='1'&&st[i]<='9')
f[1][i][0][0]=1;
for (int i1=1;i1<k;i1++)
for (int i2=1;i2<=n;i2++)
{
for (int i3=0;i3<=k;i3++)
for (int i4=0;i4<=1;i4++)
{
for (int i5=head[i2];i5!=-1;i5=e[i5].next)
{
if (check(st[i2],st[e[i5].t],i4,0))
if (st[e[i5].t]=='(')
f[i1+1][e[i5].t][i3+1][0]=(f[i1+1][e[i5].t][i3+1][0]+f[i1][i2][i3][i4])%p;
else if (st[e[i5].t]==')')
if (i3>=1)
f[i1+1][e[i5].t][i3-1][0]=(f[i1+1][e[i5].t][i3-1][0]+f[i1][i2][i3][i4])%p;
else;
else
f[i1+1][e[i5].t][i3][0]=(f[i1+1][e[i5].t][i3][0]+f[i1][i2][i3][i4])%p;
if (st[e[i5].t]=='0'&&check(st[i2],st[e[i5].t],i4,1))
f[i1+1][e[i5].t][i3][1]=(f[i1+1][e[i5].t][i3][1]+f[i1][i2][i3][i4])%p;
}
if (st[i2]!='0')
break;
}
}
for (int i=1;i<=n;i++)
if (st[i]!='+'&&st[i]!='-'&&st[i]!='*'&&st[i]!='/')
ans=(ans+f[k][i][0][0]+f[k][i][0][1])%p;;
printf("%lld",ans);
return 0;
}