1005 Little W and Contest
题目
http://acm.hdu.edu.cn/showproblem.php?pid=6795
题意
给定n个点,有两种点,权值分别为1和2,
初始时,n个点互不相连。初始时,n个点互不相连。初始时,n个点互不相连。
接着会加入n−1条边,保证每次加入的边的两个端点事先是不相连通的。
要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。
每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。
题解
初始状态的计算:假设权值为1的点的数量为cnt1,权值为2的点的数量为cnt2,
此时的不同方案的总数:
ans = C2cnt2 x C1cnt1 + C3cnt2
我们考虑每次添加一条边后,会有哪一部分的方案会变成不合法的方案
由于每次加边合并都是对两个连通块进行操作,假设现在u和v之间添加一条边
因此,我们将n个点分为3个部分:u所在连通块Gu、v所在的连通块Gv、其他剩余点Gr。
不合法的方案有以下四种:
(1)Gu选择一个权值为1的点,Gv选择一个权值为2的点,Gr选择一个权值为2的点。
(2)Gu选择一个权值为2的点,Gv选择一个权值为1的点,Gr选择一个权值为2的点。
(3)Gu选择一个权值为2的点,Gv选择一个权值为2的点,Gr选择一个权值为2的点。
(4)Gu选择一个权值为2的点,Gv选择一个权值为2的点,Gr选择一个权值为1的点。
参考博客:https://blog.csdn.net/njuptACMcxk/article/details/107641773
AC代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
int pre[maxn],a[maxn],p1[maxn],p2[maxn];
ll g1[maxn],g2[maxn];
int find(int x)//找到根节点
{
return pre[x] == x ? x : find(pre[x]);
// return pre[x] < 0 ? x : find(pre[x]);
}
void merge(int x, int y)//x和y做朋友
{
int fx = find(x), fy = find(y);//找到x和y的根节点
if (fx != fy)//x,y不是同一个人
{
// pre[fy] += pre[fx];//人数是两个之和
pre[fx] = fy;//合并
}
}
ll C2(int n)
{
if(n<2) return 0;
return (ll)(n-1)*n/2%mod;
}
ll C3(int n)
{
if(n<3) return 0;
return (ll)(n)*(n-1)*(n-2)/6%mod;
}
int main()
{
// freopen("1009.txt", "r", stdin); // 读入刚写的文件
// freopen("1009zhz.out", "w", stdout); // 输出将要输出的数据
int T;
int cnt=1;
scanf("%d",&T);
while(T--)
{
int n,cnt1=0,cnt2=0;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
pre[i]=i;
scanf("%d",&a[i]);
if(a[i]==1)
{
cnt1++;
p1[i]=1;
p2[i]=0;
}
else
{
cnt2++;
p1[i]=0;
p2[i]=1;
}
}
ll ans = (C2(cnt2)*cnt1%mod+C3(cnt2))%mod;
printf("%lld\n",ans);
for(int i=0; i<n-1; i++)
{
int u,v;
scanf("%d %d",&u,&v);
int fu=find(u),fv=find(v);
int k = 0;
k=(k+(ll)p1[fu]*p2[fv]*(cnt2-p2[fu]-p2[fv]))%mod;
k=(k+(ll)p2[fu]*p1[fv]*(cnt2-p2[fu]-p2[fv]))%mod;
k=(k+(ll)p2[fu]*p2[fv]*(cnt2-p2[fu]-p2[fv]))%mod;
k=(k+(ll)p2[fu]*p2[fv]*(cnt1-p1[fu]-p1[fv]))%mod;
ans = (ans-k+mod)%mod;
printf("%lld\n",ans);
merge(u,v);
p1[fv]+=p1[fu], p2[fv]+=p2[fu];
p1[fu]=0, p2[fu]=0;
}
}
return 0;
}
/*
1
5
2 2 2 1 1
4 5
1 4
2 1
3 2
*/
1009 Parentheses Matching
题目:
http://acm.hdu.edu.cn/showproblem.php?pid=6799
题意
给字符串s,s含有:( ) * 三种字符。你可以将 * 转化成左右括号,或者不转换。s求符合括号匹配的情况下,最小的字典序并输出。没有答案输出"No solution!"
题解
优先从左边把星星改为左括号,完成右括号的匹配。
在把从右边把星星改为右括号,完成左括号的匹配。
这样字典序就保证最小了。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
using namespace std;
typedef long long ll;
const int maxn = 100005;
char s[maxn];
int l[maxn],r[maxn],local[maxn];
int main()
{
// freopen("1009.txt", "r", stdin); // ¶áèë¸ÕD′μÄÎļt
// freopen("1009zhz.out", "w", stdout); // êä3ö½«òaêä3öμÄêy¾Y
int T;
int cnt=1;
scanf("%d",&T);
while(T--)
{
scanf("%s",s);
int len = strlen(s);
// if(cnt==8708)
// puts(s);
int lena=0,lenb=0,star=0;
for(int i=0; i<len; i++)
{
if(s[i]=='(')
l[lena++]=i+1;
else if(s[i]==')')
r[lenb++]=i+1;
else
local[star++]=i+1;
}
l[lena]=0;
r[lenb]=0;
local[star]=0;
bool falg = true;
int beg=0;
bool next = true;
for(int i=0,j=0; i<lenb; i++)
{
if(l[j]<r[i]&&l[j]!=0)
j++;
else
{
if(star==beg)
falg = false;
for(int z=beg; z<star; z++)
{
if(local[z]<r[i])
{
s[local[z]-1] = '(';
beg++;
break;
}
else if(z+1==star)
falg = false;
}
}
if(j+1>lena)
{
next = false;
}
}
if(next)
for(int i=lena-1,j=lenb-1; i>=0; i--)
{
if(l[i]<r[j])
j--;
else
{
if(star==beg)
falg = false;
for(int z=star-1; z>=beg; z--)
{
if(local[z]>l[i])
{
s[local[z]-1] = ')';
star--;
break;
}
else if(z==beg)
falg = false;
}
}
}
if(falg)
{
for(int i=0; i<len; i++)
{
if(s[i]!='*')
printf("%c",s[i]);
}
puts("");
}
else
{
puts("No solution!");
}
}
return 0;
}