20220809杭电第七场
Black Magic
题目大意:
给定n个方块,每个方块左右都可能是黑色或者白色,具体如下
E:左右两边都是白***r>L:左边是黑色,右边是白***r>R:右边是白色左边是黑***r>B:左右两边都是黑***r>给定每种方块的数量
如果相邻的两个方块邻面颜色都是黑色,那么这两个方块就可以合并
求出经过处理之后最少的方块数和最大的方块数
input:
3
1 1 1 1
1 2 3 4
3 4 5 6
output:
2 4
4 8
8 16
解:
对于连通块最多的情况,我们需要让合并的方块尽可能的少
可以把L全放在最左边,R全放在最右边,然后E和B相间摆放
对于连通块最少的情况
可以先把B全合并起来,一个L和一个R也能分为一组拼起来,注意,如果有B并且有一组L和R的话,可以把一组L和R拼在B的两边,这样可以再减少一组。
#include <bits/stdc++.h> using namespace std ; #define ll long long #define endl '\n' int p,q,i,j; ll solve2(int a,int b,int c,int d) { ll ans = 0; /*int aa=0,dd=0; int minn = min(c,b); aa = min(minn,a); b -= aa, c -= aa; minn = min(c,b); // dd= min(minn,d) ; // cout<<"dd is "<<dd<<endl; // b -= dd,c -=dd; int l = min(b,c); ans += 2*l; ans += 2*(aa+dd); /*if(b!=c) { aa += min(b,c); b -= min(b,c); c -= min(b,c); ans += max(b,c) - 1; a++; if(d>a) ans--; } //if(b==c) ans+=b*2; ans += min(a,d)*2; if(a>d) ans+=a-d; if(d>a && a!=0) ans+=1; */ ans += c + b ; if(d>a) ans+=2*a+1; else ans += a+d; // cout<<a<<" "<<b<<" "<<c<<" "<<d<<" "<<endl; // cout<<aa<<" "<<dd<<" "<<endl; return ans; } ll solve1(int a,int b,int c,int d) { swap(a,d); ll ans = 0; if(a>0) a=1; if(b>0 || c>0) a=0; ans += d + a + max(b,c); return ans; } void solve() { cin>>p>>q>>i>>j; cout<<solve1(p,q,i,j)<<" "<<solve2(p,q,i,j)<<endl; } int main () { ios::sync_with_stdio(false) ,cin.tie(0); int t; cin>>t; while(t--) { solve(); } }
Triangle Game
题目大意:
给定三个正整数abc代表三角形的三条边,两个人玩游戏,每回合需要挑选一条边并且让这条边减去>=1的值,如果在某个回合操作结束后三条边不能构成三角形,则游戏结束,问先手的人是否有必胜的策略
input:
3
2 2 3
2 3 4
5 3 4
output:
Win
Lose
Win
解:
结论:当(a-1)^(b-1)^(c-1)!=0时先手必胜
#include<bits/stdc++.h> using namespace std; int main() { int t, a, b, c; cin >> t; while (t--) { cin >> a >> b >> c; if (((a - 1) ^ (b - 1) ^ (c - 1)) != 0) { cout << "Win" << endl; } else { cout << "Lose" << endl; } } }
Count Stickmen
题目大意:
给你一颗树,问你树上有多少个火柴人,如下所示
如图所示
以3,5这条骨架枚举
我们应该从一个火柴人的3,5两个部位下手
先看5下面的7和8,这是火柴人的两条腿,这是比较好求的,就是从5的所有边中减去连向3的那条边并且从剩下的边里面挑两条,用组合数计算
在看与3相连的两条手臂4,5以及9,10,这也比较好求,就是枚举3的所有邻点,将所有邻点的边数相加,最后减去3的边数,得到的结果就是与3点距离为2的点的个数,可以看图理解。但是这中点不能和5相连,因为和5相连的会是腿,所以要减去(5的边数-1)。在这些点里面选出两个点,用组合数求解。
但是如果两条手臂相同,即选到了像腿一样的两条手臂,我们必须减去这种情况,这种情况方案数就是从3的邻点中除了腿去枚举,将枚举的点的边数减一,得到的就是每个点下面有多少边,从这些边中选出两条来,最后将结果累加就是多余的情况。
选择头的话方案数明显是3的边数-3(身体和两只手臂)。
#include<bits/stdc++.h> using namespace std; #define int long long vector<int> v[500005]; int edges[500005];//每个点有多少条边 int help1[500005];//du-dx int help2[500005];//两个手臂相同的方案数 const int mod = 998244353; int n, temp1, temp2; int add(int a, int b) { return (a += b) >= mod ? a - mod : a; } int sub(int a, int b) { return (a -= b) < 0 ? a + mod : a; } int C(int x) { return x * (x - 1) / 2 % mod; } void solve() { cin >> n; for (int i = 1; i <= n; i++) { v[i].clear(); help1[i] = 0; help2[i] = 0; edges[i] = 0; } for (int i = 1; i <= n - 1; i++) { cin >> temp1 >> temp2; v[temp1].push_back(temp2); v[temp2].push_back(temp1); edges[temp1]++; edges[temp2]++; } for (int i = 1; i <= n; i++) { for (auto &y: v[i]) { help1[i] = add(help1[i], edges[y] - 1); help2[i] = add(help2[i], C(edges[y] - 1)); } // help2[i] = sub(help2[i], C(edges[i] - 1)); } int ans = 0; int legs, hands, head; for (int i = 1; i <= n; i++) { if (edges[i] >= 4) {//对类似样例中点3这样的点枚举,只有边数大于等于四才有可能成为点三那样的点 for (auto &j: v[i]) {//枚举这个点所有的邻点 if (edges[j] >= 3) {//先找到腿的节点 head = sub(edges[i], 3); legs = sub(edges[j], 1); hands = sub(help1[i], legs); legs = C(legs); hands = sub(C(hands), sub(help2[i], C(edges[j] - 1))); ans = add(ans, head * hands % mod * legs % mod); } } } } cout << ans << endl; } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } }#ACM##菜鸡的求救#