题目链接:http://codeforces.com/contest/766
A. Mahmoud and Longest Uncommon Subsequence
解法:水题,如果两个字符串不完全一样的话,最长不同子串肯定是其中最长的字符串。
using namespace std;
int main(){
string a,b;
cin>>a>>b;
if(a==b) puts("-1");
else cout<<max(a.size(),b.size())<<endl;
return 0;
}
B. Mahmoud and a Triangle
题意:给你N条边,让你判断是否能够用其中三条边整出来一个三角形。
解法:直接将边按照从小到大排序。然后相邻的三条边进行判断即可。脑补理由。
using namespace std;
const int maxn = 1e5+7;
int n, a[maxn];
int main(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a+1, a+n+1);
int flag = 0;
for(int i = 2; i <= n-1; i++){
if(a[i]+a[i-1] > a[i+1]) flag = 1;
}
if(flag) puts("YES");
else puts("NO");
}
C. Mahmoud and a Message
题意:给定一个小写字母组成的字符串和每个小写字母最多存在长度为a[i]的子串中,输出满足条件的分割方案数,并输出所有方案中最长子串的长度和最少的分割次数。
解法:设dp【i】表示前 i 个字母中有多少种合法的方案,则总方案数就是dp【n】。那么方案转移方程:dp【i】=dp【i】+dp【j】,注意转移方程的条件。
using namespace std;
const int mod=1e9+7;
int a[35];
int dp[3][3000];
int main()
{
int n;
cin>>n;
string s;
cin>>s;
for(int i=0;i<26;i++) cin>>a[i];
dp[0][0]=1;
for(int i=1;i<=n;i++){
int len=mod;
dp[1][i]=-mod;
dp[2][i]=mod;
for(int j=i-1;j>=0;--j)
{
len=min(len,a[s[j]-'a']);
if(len>=i-j)
{
//方案数
dp[0][i]=(dp[0][i]+dp[0][j])%mod;
//子串最大长度
dp[1][i]=max(dp[1][i],max(i-j,dp[1][j]) );
//最小子串数
dp[2][i]=min(dp[2][i],dp[2][j]+1);
}
}
}
cout<<dp[0][n]<<endl<<dp[1][n]<<endl<<dp[2][n]<<endl;
return 0;
}
D. Mahmoud and a Dictionary
题意:给你n个串,然后给你m个关系,p个询问。 每个关系告诉你两个单词是同义还是反义的,如果与之前的不矛盾的话,输出yes;如果矛盾,就忽略这次操作,输出no。 对于每个询问,如果两个单词是同义输出1,反义输出2,不知道输出3.
解法:POJ食物链经典套路。我们用并查集去做,让2i表示这个词,2i-1表示这个词的反义。如果i和j同义,就说明2i和2j一样,2i-1和2j-1一样。如果反义,就说明2i-1和2j一样,2i和2j-1一样。 然后check就好了。
using namespace std;
const int maxn = 1e6+7;
int n, m, q;
map <string, int> mp;
string s[maxn];
namespace dsu{
int fa[maxn];
inline void init(){for(int i = 1; i <= 2*n; i++) fa[i] = i;}
inline int find_set(int x){if(x == fa[x]) return x; else return fa[x] = find_set(fa[x]);}
inline void union_set(int x, int y){int fx = find_set(x), fy = find_set(y); if(fx != fy) fa[fx] = fy;}
}
using namespace dsu;
int main()
{
scanf("%d%d%d", &n, &m, &q);
init();
for(int i = 1; i <= n; i++) cin >> s[i], mp[s[i]] = i;
for(int i = 1; i <= m; i++){
int cmd; string a, b;
cin >> cmd >> a >> b;
int id1 = mp[a], id2 = mp[b];
if(cmd == 1){//同意
if(find_set(id1*2) == find_set(id2*2-1)){
puts("NO");
continue;
}
if(find_set(id1*2-1) == find_set(id2*2)){
puts("NO");
continue;
}
puts("YES");
union_set(id1*2, id2*2);
union_set(id1*2-1, id2*2-1);
}
else{//反义
if(find_set(id1*2) == find_set(id2*2)){
puts("NO");
continue;
}
if(find_set(id1*2-1) == find_set(id2*2-1)){
puts("NO");
continue;
}
puts("YES");
union_set(id1*2, id2*2-1);
union_set(id1*2-1, id2*2);
}
}
while(q--)
{
string a, b;
cin >> a >> b;
int id1 = mp[a], id2 = mp[b];
if(find_set(2*id1) == find_set(2*id2)) puts("1");
else if(find_set(2*id1-1) == find_set(2*id2-1)) puts("1");
else if(find_set(2*id1-1) == find_set(2*id2)) puts("2");
else if(find_set(2*id1) == find_set(2*id2 - 1)) puts("2");
else puts("3");
}
return 0;
}
E. Mahmoud and a xor trip
题意:给定一颗n节点的树以及每个节点的权值,另dis(u,v)表示节点u到v路径上的异或和,求不大于i的节点与i组成的有序对的距离的和(1<=i<=n)。
解法:位运算的话大多可以想到按位拆分,统计每一位对答案的贡献,因为每一位的运算都是独立的。所以按位枚举,假设当前是第b位,则dp[x][0]表示以x为根节点的异或值为0的路径的数量,dp[x][1]也是如此定义。
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
LL dp[maxn][2], a[maxn];
LL ans = 0;
int head[maxn], edgecnt;
struct edge{
int to,next;
}E[maxn*2];
void init(){
memset(head,-1,sizeof(head));
edgecnt=0;
}
void add(int u,int v){
E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void dfs(int x, int fa, int bit){
LL q = 0;
LL b = (a[x] >> bit) & 1;
dp[x][b] = 1;//init
dp[x][b^1] = 0;
for(int i=head[x]; ~i; i=E[i].next){
int v = E[i].to;
if(v == fa) continue;
dfs(v, x, bit);
q += dp[x][0] * dp[v][1] + dp[x][1] * dp[v][0];//统计子节点到x的路径上异或和,只需算1^0与0^1即可
dp[x][b^0] += dp[v][0];//更新异或操作后的状态值。
dp[x][b^1] += dp[v][1];
}
ans += (q << bit);
}
int main(){
int n;
init();
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%lld", &a[i]), ans += a[i];
for(int i=1; i<n; i++){
int u, v;
scanf("%d %d", &u,&v);
add(u, v);
add(v, u);
}
for(int i=0; i<=20; i++) dfs(1, -1, i);
printf("%lld\n", ans);
return 0;
}