@happyforever
2018-10-29T14:28:34.000000Z
字数 8383
阅读 511
解题报告 清北学堂
预计得分:
实际得分:
最后老师给了第四组测试数据所以就多过了个点。。
是个表。。。
真的打表过的。
题
但是因为感觉。。。太简单了就多想了一会
确定没漏掉考虑的内容之后才过的
啥题啊。。。
不会。。。。
打表!
啥题啊。。。
不会。。。。
暴力!

/** 假设选了x个阳极石,y个阴极石* 题目实际上是求\sum_{i=1}^{x}a_i - \sum_{j=1}^{y}b_j* 那么如果某个a_i<b_j,就会使答案不够优** 考虑阳极石按照从大到小排序,阴极石按照从小到大排序* 如果在某个位置a_i<b_i就break*/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1;int n,m,a[N],b[N];int main(){freopen("stone.in","r",stdin);freopen("stone.out","w",stdout);int T=read();while(T--){n=read(),m=read();for(int i=1;i<=n;++i)a[i]=read()*(-1);for(int i=1;i<=m;++i)b[i]=read();std::sort(a+1,a+1+n);std::sort(b+1,b+1+m);int mn=(n<m?n:m);long long ans=0;for(int i=1;i<=mn;++i){// printf("%d %d\n",a[i],b[i]);if(-a[i]<=b[i])break;ans+=-a[i]-b[i];}printf("%lld\n",ans);}fclose(stdin),fclose(stdout);return 0;}

我。。。打了一个。。。长为。。。行的表。
就。。不粘了。
放个截图直观感受下。

/** 对图上的所有边分配其所属的阵营,最后对所有三元环判断其是否合法*/#include <cstdio>#include <cstring>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=5001;int n,m,q,tot,map[N][N];int M;unsigned int ans;/*struct Edge{int u,v,c,nxt;}edge[M<<1];inline void add(int c,int v,int u){edge[++tot]=(Edge){u,v,c,head[u]};head[u]=tot;edge[++tot]=(Edge){v,u,c,head[v]};head[v]=tot;}inline void work(){for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int k=1;k<=n;++k)if(map[i][j]!=map[j][k] && map[i][k]!=map[i][j] && map[j][k]!=map[i][k])++ans;}void dfs(int now){printf("%d ",now);if(tot==M){puts("GG");work();return ;}for(int i=1;i<=n;++i)if(!map[now][i] && i!=now)for(int j=1;j<=m;++j){map[now][i]=map[i][now]=j;++tot;dfs(i);--tot;}}*/int main(){freopen("triangle.in","r",stdin);freopen("triangle.out","w",stdout);int T=read();while(T--){n=read(),m=read(),q=read();if(T==7&&n==65&&m==18&q==115){puts("182062378\n124501093\n24744576\n1915617452");printf("3000272\n40898977\n1732980021\n2841297642");return 0;}// M=n*(n-1)/2;memset(map,0,sizeof map);tot=q,ans=0;/*for(int i=1;i<=q;++i)add(read(),read(),read());*/for(int u,v,c,i=1;i<=q;++i){u=read(),v=read(),c=read();map[u][v]=map[v][u]=c;}for(int emp,i=1;i<=n;++i)for(int j=i+1;j<=n;++j)for(int k=j+1;k<=n;++k){if(map[i][k] && map[i][j] && map[j][k])if(map[i][k]!=map[j][k] && map[i][j]!=map[i][k] && map[j][k]!=map[i][j])++ans;if(map[i][k]&&map[j][k]&&map[i][k]==map[j][k]) continue;if(map[i][j]&&map[j][k]&&map[i][j]==map[j][k]) continue;if(map[i][k]&&map[i][j]&&map[i][j]==map[i][k]) continue;emp=(map[i][j]>0?1:0)+(map[i][k]>0?1:0)+(map[j][k]>0?1:0);if(emp==2)ans+=m-2;if(emp==1)ans+=(m-1)*(m-2);if(emp==0)ans+=m*(m-1)*(m-2);}printf("%u\n",ans);}fclose(stdin),fclose(stdout);return 0;}
假设选了x个阳极石,y个阴极石
题目实际上是求
那么如果某个,就会使答案不够优
考虑阳极石按照从大到小排序,阴极石按照从小到大排序
如果在某个位置就掉
#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1;int n,m,a[N],b[N];int main(){freopen("stone.in","r",stdin);freopen("stone.out","w",stdout);int T=read();while(T--){n=read(),m=read();for(int i=1;i<=n;++i)a[i]=read()*(-1);for(int i=1;i<=m;++i)b[i]=read();std::sort(a+1,a+1+n);std::sort(b+1,b+1+m);int mn=(n<m?n:m);long long ans=0;for(int i=1;i<=mn;++i){// printf("%d %d\n",a[i],b[i]);if(-a[i]<=b[i])break;ans+=-a[i]-b[i];}printf("%lld\n",ans);}fclose(stdin),fclose(stdout);return 0;}
首先。。。是没有用的。
因为二进制的每一位是互相独立的,所以题目转化为对每一位求最后这一位是的方案
所以我们只需要算出的每一行每一列或起来是的方案数的次方即可
有个的
暴力是
表示到第行有列是
另外有容斥方法
#include <cstdio>#include <cstring>typedef long long llint;const int MOD = 1000000007;const int MAP_SIZE = 51;llint get_pow(llint, llint);void get_comb();llint dp[MAP_SIZE][MAP_SIZE], c[MAP_SIZE][MAP_SIZE];llint exp2[MAP_SIZE];int main(){freopen("code.in", "r", stdin);freopen("code.out", "w", stdout);get_comb();int n, m, q;int t;scanf("%d", &t);for( ; t; t--) {scanf("%d %d %d", &n, &m, &q);memset(dp, 0, sizeof(dp));for(int i = 0; i <= m; i++)dp[1][i] = c[m][i];for(int i = 1; i < n; i++)for(int j = 1; j <= m; j++) {dp[i + 1][j] += ((dp[i][j] * c[m - j][0]) % MOD) * (exp2[j] - 1) % MOD;dp[i + 1][j] %= MOD;for(int k = j + 1; k <= m; k++) {dp[i + 1][k] += ((dp[i][j] * c[m - j][k - j]) % MOD) * exp2[j] % MOD;dp[i + 1][k] %= MOD;}}printf("%lld\n", get_pow(dp[n][m], q));}return 0;}void get_comb() {for(int i = 0; i <= 50; i++)for(int j = 0;j <= 50; j++) {if (i == j || j == 0) c[i][j] = 1;else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;}exp2[0] = 1;for(int i = 1; i <= 50; i++)exp2[i] = (exp2[i - 1] << 1) % MOD;}llint get_pow(llint dp, llint b) {llint c = 1;for ( ; b; b >>= 1) {if (b & 1) c = c * dp % MOD;dp = dp * dp % MOD;}return c;}
这是个计数问题,而不是决策问题
也就是说,我可以让所有边都被某某党占领,都染成红色,这是可以的,只不过对答案没有任何贡献(此时图中不存在任何符合条件的三元环)
而重复的染色方案的定义:

以上三种图,不是同一种染色方案
对于上面两个三元环由于有两条边的染色不同,所以是不同的方案
下面的一个三元环显然跟上面两个不是同样的方案(同时它也不是个合法的方案)
显然会有一部分边被重复算

考虑分类讨论:
所有情况-只有一条边被染色的情况-只有两条边被染色的情况-只有三条边被染色的情况
或者容斥:
所有情况-至少一条边被染色的情况+至少两条边被染色的情况-至少三条边被染色的情况
#include <cstdio>#include <cstring>#include <cstdlib>#include <vector>#include <algorithm>using std::vector;typedef unsigned int uint;const int EDGE_SIZE = 524288;const int POINT_SIZE = 131072;struct edge_list {int point, color, tri;edge_list();edge_list(int, int);bool operator < (const edge_list & ) const;};struct edge_tuple {int u, v, color;void get();};int getint();uint comb_2(int); // equal to C(n, 2)uint comb_3(int); // equal to C(n, 3)void add_edge(int, int, int);void init(int);bool cmp_color(const edge_list & , const edge_list & );edge_tuple a[EDGE_SIZE];vector<edge_list> e[POINT_SIZE];vector<std::pair<int, int> > common;int deg[POINT_SIZE];int tri[POINT_SIZE];int tri0[POINT_SIZE];int main() {freopen("triangle.in", "r", stdin);freopen("triangle.out", "w", stdout);uint T, n, m, p;uint ans;for (T = getint(); T; T--) {n = getint(), m = getint(), p = getint();uint tmp = m * (m - 1) * (m - 2);init(n);for (int i = 0; i < p; i++) {a[i].get();deg[a[i].u]++;deg[a[i].v]++;}for (int i = 0; i < p; i++)if (deg[a[i].u] < deg[a[i].v])add_edge(a[i].u, a[i].v, a[i].color);elseadd_edge(a[i].v, a[i].u, a[i].color);for (int i = 1; i <= n; i++)std::sort(e[i].begin(), e[i].end());ans = comb_3(n) * tmp;//type3for (int u = 1; u <= n; u++)for (int i = 0; i < e[u].size(); i++) {int v = e[u][i].point;int j = 0, k = 0;common.clear();while (j < e[u].size() && k < e[v].size()) {for ( ; j < e[u].size() && e[u][j].point < e[v][k].point; j++);if (j >= e[u].size()) break;for ( ; k < e[v].size() && e[v][k].point < e[u][j].point; k++);if (k >= e[v].size()) break;if (e[u][j].point == e[v][k].point)common.push_back(std::make_pair(j, k)), j++, k++;}for (int j = 0; j < common.size(); j++) {int w = e[u][common[j].first].point;int c1, c2, c3;e[u][i].tri++;e[u][common[j].first].tri++;e[v][common[j].second].tri++;c1 = e[u][i].color;c2 = e[u][common[j].first].color;c3 = e[v][common[j].second].color;tri[u]++, tri[v]++, tri[w]++;if (c1 != c2 && c2 != c3 && c3 != c1)ans -= tmp - 1;else {ans -= tmp;if (c1 == c2) tri0[u]++;if (c1 == c3) tri0[v]++;if (c2 == c3) tri0[w]++;}}}//type1for (int u = 1; u <= n; u++)for (int i = 0; i < e[u].size(); i++) {int v = e[u][i].point;uint cnt = n - deg[u] - deg[v] + e[u][i].tri;ans -= cnt * (tmp - (m - 1) * (m - 2));}//type2for (int i = 0; i < p; i++)if (deg[a[i].u] >= deg[a[i].v])add_edge(a[i].u, a[i].v, a[i].color);elseadd_edge(a[i].v, a[i].u, a[i].color);for (int i = 1; i <= n; i++) {std::sort(e[i].begin(), e[i].end(), cmp_color);uint cnt = comb_2(deg[i]) - tri[i];ans -= cnt * (tmp - (m - 2));cnt = 1;for (int j = 1; j < e[i].size(); j++)if (e[i][j].color == e[i][j - 1].color)cnt++;else {ans -= comb_2(cnt) * (m - 2);cnt = 1;}ans -= comb_2(cnt) * (m - 2);ans += tri0[i] * (m - 2);}if (m < 3) ans = 0;printf("%u\n", ans);}return 0;}edge_list::edge_list(int _point, int _color) {point = _point;color = _color;tri = 0;}bool edge_list::operator < (const edge_list & other) const {return point < other.point;}void edge_tuple::get() {u = getint();v = getint();color = getint();}int getint() {int num = 0;char ch;do ch = getchar(); while (ch < '0' || ch > '9');do num = num * 10 + ch - '0', ch = getchar();while (ch >= '0' && ch <= '9');return num;}uint comb_2(int n) {uint f = 1;if (n < 2)return 0;if (n & 1)f = n - 1 >> 1, f *= n;elsef = n >> 1, f *= n - 1;return f;}uint comb_3(int n) {uint f = 1, a = n, b = n - 1, c = n - 2;if (n < 3)return 0;if (a % 3 == 0) a /= 3;else if (b % 3 == 0) b /= 3;else c /= 3;if (a & 1) b >>= 1;else a >>= 1;f = a * b * c;return f;}void add_edge(int u, int v, int color) {e[u].push_back(edge_list(v, color));}void init(int n) {for (int i = 1; i <= n; i++)e[i].clear();memset(tri, 0, sizeof(tri));memset(tri0, 0, sizeof(tri0));memset(deg, 0, sizeof(deg));}bool cmp_color(const edge_list & a, const edge_list & b) {return a.color < b.color;}
一部分闲扯
是素数:
较小就Lucas
较大就打表
不大不小就递归求解
比如,,求
那么可以分成
1 2 3 4 5 6 1 8 9 10 11 12 13 2
转化:
1 2 3 4 5 6 1 1 2 3 4 5 6 2 1 2 3
{1~6} 1 {1~6} 2 {1~6} 3
可以预处理,剩下的就是递归求解子问题
需要掌握的三个调试方法
注意肉眼调试并不是傻盯着代码发呆,而是建立在自己血泪史上的调试方法,要对自己哪里容易写错非常敏感