UESTC 2016 Summer Training #19 Div.2(未完待续)
来源:互联网 发布:食品加工控制软件 编辑:程序博客网 时间:2024/05/16 06:38
题目来源:
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=643
2014 Asia Jakarta Regional Contest
UVALive 6906 - 6916
A
B
题意:
给出一个哑铃图的定义. 一个图中分成点个数相等的两堆,每堆内部是一个完全图.堆与堆之间只有一条边相连. 现在给出一个图,不一定联通. 看它的每一个联通块是不是我们的哑铃图. 输出满足哑铃图的个数.
题解:
法一:用桥来判断. 哑铃图一定只有一个桥吗?发现只有一个特例,两边是2个点的时候都是桥. 其他情况有且只有一个桥. 那么有一个桥的一定是哑铃图吗?还要求桥的两边点数一样,并且边数是一个定值.用度来统计就好了.
所以就是先dfs tarjan找一下桥,找到桥之后看两边的点数和度数判定.额外的是4个点的特判.
法二:只从度的角度来判断.发现特殊的两个点的度数是比其他的都多1的.并且其他的点的度数都是确定的.那么我们判定一下先仅有两个特殊的度点.然后从其中的一个点,刨去另一个点不能跑,只跑一边去染色,然后先看度满足情况.之后再看每个点是不是只和自己颜色的染过.
///////////代码是判定桥的.#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int N = 1e3;int n,m;int vis[N];int dfn[N],low[N],in[N];int key[10010],to;vector <int> g[N];int flag = 0;int cnt = 0,cn = 0;int dfs(int u,int f,int len){ cn += in[u]; vis[u] = 1; low[u] = len; dfn[u] = len; int tot = 0; for (int i = 0;i < g[u].size();i++) { int v = g[u][i]; if (v == f) continue; if (vis[v] == 0) { int tmp = dfs(v,u,len+1); tot += tmp; if (low[v] > dfn[u]) key[++to] = tmp; low[u] = min(low[v],low[u]); } else low[u] = min(dfn[v],low[u]); } return tot + 1;}int main(){ int t; scanf("%d",&t); while (t--) { memset(vis,0,sizeof vis); memset(in,0,sizeof in); scanf("%d %d",&n,&m); for (int i = 1;i <= n;i++) g[i].clear(); int ans = 0; for (int i = 1;i <= m;i++) { int x,y; scanf("%d %d",&x,&y); in[x]++;in[y]++; g[y].push_back(x); g[x].push_back(y); } for (int i = 1;i <= n;i++) if (vis[i] == 0) { cnt = cn = to = 0; cnt = dfs(i,0,1); if (cnt & 1) continue; if (cn & 1) continue; int flag2 = 0; cnt /= 2; for (int i = 1;i <= to;i++) if (key[i] == cnt) flag2 = 1; if (flag2 == 1 && cn/2 == 1 + cnt*(cnt-1)) ans++; } static int ca = 0; printf("Case #%d: %d\n",++ca,ans); }}
C
题意:
给定N≤103个障碍物,和一辆车,变挡次数K≤10,车的电量E≤50,车有4挡,耗电为挡数,不同的挡提供不同的额外能量
能量不足以提供车当前挡前进的时候,车直接就报废了。问穿过所有的障碍物人的最少能量花费?
分析:
赤果果的dp
dp[i][k][e][l]:=第几个障碍物,剩余变挡次数,剩余能量,当前挡数,人的最小能量花费,由于状态1000∗10∗50∗4=2e6比较大,乘case数=100就T了
用队列优化一下就好了,类似于spfa的感觉
//// Created by TaoSama on 2015-12-21// Copyright (c) 2015 TaoSama. All rights reserved.////#pragma comment(linker, "/STACK:1024000000,1024000000")#include <algorithm>#include <cctype>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iomanip>#include <iostream>#include <map>#include <queue>#include <string>#include <set>#include <vector>using namespace std;#define pr(x) cout << #x << " = " << x << " "#define prln(x) cout << #x << " = " << x << endlconst int N = 1e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;int n, k, e, ans;int h[N], f[N][11][51][4];bool in[N][11][51][4];int power[] = {0, 4, 8, 11};struct Sta { int p, c, e, l; //position, chance, energy, level};void see(Sta u, Sta v) { int &cur = f[u.p][u.c][u.e][u.l]; int &nxt = f[v.p][v.c][v.e][v.l]; printf("f[%d][%d][%d][%d] = %d → ", u.p, u.c, u.e, u.l, cur); printf("f[%d][%d][%d][%d] = %d\n", v.p, v.c, v.e, v.l, nxt);}void spfa() { queue<Sta> q; memset(f, 0x3f, sizeof f); memset(in, false, sizeof in); f[0][k][e][0] = 0; in[0][k][e][0] = true; q.push((Sta) {0, k, e, 0}); while(q.size()) { Sta u = q.front(); q.pop(); in[u.p][u.c][u.e][u.l] = false; int &cur = f[u.p][u.c][u.e][u.l]; if(u.p == n) { ans = min(ans, cur); continue; } if(u.c) { for(int i = 0; i < 4; ++i) { if(i == u.l || u.e < i) continue; Sta v = u; ++v.p; --v.c; v.e -= i; v.l = i; int cost = max(0, h[v.p] - power[v.l]); int &nxt = f[v.p][v.c][v.e][v.l]; if(nxt > cur + cost) { nxt = cur + cost;// see(u, v); if(!in[v.p][v.c][v.e][v.l]) { in[v.p][v.c][v.e][v.l] = true; q.push(v); } } } } Sta v = u; ++v.p; if(v.e < v.l) v.c = v.e = v.l = 0; //lack energy, just set 0 else v.e -= v.l; int cost = max(0, h[v.p] - power[v.l]); int &nxt = f[v.p][v.c][v.e][v.l]; if(nxt > cur + cost) { nxt = cur + cost; if(!in[v.p][v.c][v.e][v.l]) { in[v.p][v.c][v.e][v.l] = true; q.push(v); } } }}int main() {#ifdef LOCAL freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);#endif ios_base::sync_with_stdio(0); int t; scanf("%d", &t); while(t--) { scanf("%d%d%d", &n, &k, &e); for(int i = 1; i <= n; ++i) scanf("%d", h + i); ans = INF; spfa(); static int kase = 0; printf("Case #%d: %d\n", ++kase, ans); } return 0;}
D
/* * 组合数学 * 题意: * 有N(500)个人,编号1~N,忽略前K(1<=K<N)的单调性,求使P(1<=P<=N)为第一个数小于前一个数或者最后一个数的 * 排列方案数。 * 思路: * 根据样例可知,上升序列从第K位开始计算。 * [ K-1 ][ 上升序列 ] P [ 任意 ] * 或 * [ K-1 ][ 上升序列,P ] * 对于排列1,需要枚举上升序列的长度,记为L(1<=L<=N-K), * 因为P小于前一个数,所以L必然含有[P+1,N]的数,记为I(1<=I<=min(N-P,L)) * 那么方案数:∑∑C(N-P,I)*C(P-1,L-I)*P(N-1-L),剩下N-1-L个数任意排列 * 对于排列2,因为后半段是上升序列,所以[P+1,N]必需全部放入K-1中, * 方案数:C(K-1,N-P)*P(N-P)*C(P-1,K-1-(N-P))*P(K-1-(N-P)) */#include<cstdio>#include<cstring>#include<cmath>#include<set>#include<algorithm>using namespace std;#define LL long long#define N 507#define MOD 1000000007LL a[N];LL c[N][N];void init(){ LL i,j; // A a[0]=1; for(i=1;i<N;++i) a[i]=(a[i-1]*i)%MOD; // C for(i=0;i<N;++i) for(j=0;j<=i;++j) if(j==0 || j==i){ c[i][j]=1; } else{ c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD; }}int main(){ int T,tt=0; int n,m,p; int i,j; LL ans; init(); scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&p); ans=0; // end if(n-p<m){// ans=a[n-p]*c[p-1][m-1-(n-p)]%MOD*a[m-1-(n-p)]%MOD; ans=c[m-1][n-p]*a[n-p]%MOD*c[p-1][m-1-(n-p)]%MOD*a[m-1-(n-p)]%MOD; } // between for(j=1;j<=n-m;++j){ for(i=1;i<=min(n-p,j);++i){ ans=(ans+c[n-p][i]*c[p-1][j-i]%MOD*a[n-1-j]%MOD)%MOD; } } printf("Case #%d: %lld\n",++tt,ans); } return 0;}
E
别人的解法:
【题意】:
给出多棵树和两类操作:操作(C x)删除结点 x 与其父结点的连边;操作(Q a b)询问 a b 是否连通。
【解题思路】:
连通性的查询容易想到用并查集,关键在于如何处理删边。
考虑到删边的难点在于查询时的路径压缩导致某些结点与其父结点”不直接相连”,这里使用离线处理,在查询之前把所有该删的边删除,同时逆序处理询问操作;当逆序处理到删边操作时,复原删掉的边(删除变为增边)。
【代码】:(上了个比较标准的并查集模板)
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<stack>#include<algorithm>#define LL long long#define maxn 25000#define IN freopen("in.txt","r",stdin);using namespace std;struct Union_Find_Set{ int fa[maxn]; /*每个结点的父亲节点编号*/ int rank[maxn]; /*树的高度*/ /*构造并查集并初始化*/ void make_set() { for(int i=0; i<maxn; i++){ fa[i] = i; /*初始时本身构成一个集合,根为本身*/ rank[i] = 0; } } /*递归查找结点所在树的根节点*/ int find_set(int x) { /*路径压缩*/ return x!=fa[x]? fa[x]=find_set(fa[x]) : x; } /*合并两个集合*/ void unite_set(int x, int y) { x = find_set(x); y = find_set(y); /*记录树的高度防止合并后退化,rank小的向rank大的连接*/ if(rank[x] < rank[y]) swap(x,y); fa[y] = x; /*合并*/ if(rank[x] == rank[y]) rank[x]++; /*高度相同则加1*/ } /*判断两结点是否属于同一集合*/ bool same_set(int x, int y) { return find_set(x) == find_set(y); }}UFS;int n,q;struct node{ char type; int first, second;};stack<node> s;stack<bool> ans;int main(int argc, char const *argv[]){ //IN; int t,ca=1;scanf("%d",&t); while(t--) { scanf("%d %d",&n,&q); while(!s.empty()) s.pop(); while(!ans.empty()) ans.pop(); UFS.make_set(); for(int i=1; i<=n; i++){ scanf("%d",&UFS.fa[i]); if(!UFS.fa[i]) UFS.fa[i] = i; } for(int i=1; i<=q; i++) { node tmp_node; getchar(); scanf("%c",&tmp_node.type); if(tmp_node.type=='Q'){ scanf("%d %d",&tmp_node.first, &tmp_node.second); } else{ scanf("%d",&tmp_node.first); tmp_node.second = UFS.fa[tmp_node.first]; /*离线处理--询问之前删边,避免路径压缩导致删边失效*/ UFS.fa[tmp_node.first] = tmp_node.first; } s.push(tmp_node); } while(q--) { node tmp_node = s.top(); s.pop(); if(tmp_node.type=='Q'){ if(UFS.same_set(tmp_node.first, tmp_node.second)) ans.push(1); else ans.push(0); } else{ UFS.fa[tmp_node.first] = tmp_node.second; } } printf("Case #%d:\n", ca++); while(!ans.empty()) { if(ans.top() == 1) puts("YES"); else puts("NO"); ans.pop(); } } return 0;}
我的做法:
由于输入只是给了每个点的父亲节点,套用并查集的思想,但是不进行路径压缩,按照题意模拟即可。
#include <bits/stdc++.h>#define _ ios_base::sync_with_stdio(0);cin.tie(0);#define INF 0x3f3f3f3f#define eps 1e-6typedef long long LL;const double pi = acos(-1.0);const long long mod = 1e9 + 9;using namespace std;const int MAX = 20005;int p[MAX];int Find(int x){ return p[x] == x ? x : Find(p[x]);}int main(){ ios_base::sync_with_stdio(false); cin.tie(0); //freopen("int.txt","r",stdin); //freopen("out.txt","w",stdout); int cnt = 0; int T; cin >> T; while(T--) { printf("Case #%d:\n",++cnt); int N,K; cin >> N >> K; int x; for(int i = 1;i <= N;i++) { cin >> x; if(x == 0) p[i] = i; else p[i] = x; } char s[5]; for(int i = 0;i < K;i++) { cin >> s; int a,b; if(s[0] == 'Q') { cin >> a >> b; int u = Find(a); int v = Find(b); if(u != v) puts("NO"); else puts("YES"); } else { cin >> x; p[x] = x; } } } return 0;}
F
G
/* * 数论 * 题意: * 给N(<=1000)盏灯,和K(<=1000)个质数。起初每盏灯都是暗的,每个质数可以改变位置是其倍数的灯的状态(暗变亮、亮 * 变暗),求最多能点亮多少盏灯。 * 思路: * 如果两个质数会改变同一盏灯,那么满足a*b<=n。 * 通过这个式子,可以发现,如果质数x和y均大于sqrt(n),那么x、y不会冲突(就是x选了,选y不会影响x点的灯)。 * 而N才1000,[sqrt(N)] = 31, * 不大于31的质数:2、3、5、7、11、13、17、19、23、29、31共11个。 * 对于大于sqrt(n)的数,取不取该数取决于小于sqrt(n)的数。 * 所以对于前11个质数,枚举其状态(取或不取)。 * 剩下的数则根据取之后会不会使答案更优来决定其状态。 */#include<cstdio>#include<cstring>#include<cmath>#include<set>#include<algorithm>using namespace std;#define N 1007int n,m,p,ans;int a[N];int st[N];set<int> g;set<int>::iterator it;void dfs(int t){ int i; if(t>=p){ int j,cnt; for(i=t;i<m;++i){ cnt=0; for(j=a[i];j<=n;j+=a[i]){ cnt+=1-(st[j]<<1); } if(cnt>0){ for(j=a[i];j<=n;j+=a[i]) st[j]^=1; } } cnt=0; for(j=1;j<=n;++j) cnt+=st[j]; ans=max(ans,cnt); return ; } // yes for(i=a[t];i<=n;i+=a[t]) st[i]^=1; dfs(t+1); for(i=a[t];i<=n;i+=a[t]) st[i]^=1; // no dfs(t+1);}int main(){ int T,tt=0; int i,x; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); g.clear(); for(i=0;i<m;++i){ scanf("%d",&x); g.insert(x); } p=g.size(); x=sqrt((double)n); for(it=g.begin(),m=0;it!=g.end();++it,++m){ a[m]=*it; if(a[m]>x) p=min(p,m); } ans=0; memset(st,0,sizeof(st)); dfs(0); printf("Case #%d: %d\n",++tt,ans); } return 0;}
H
I
/* * 状压DP * 题意: * 在一个N*M(N,M<=8)矩阵,最多放K(K<=N*M)个障碍 * 求不存在从(1,1)到(N,M)的矩阵个数(只能向下或向右移动)。 * 思路: * 因为最大就8*8,所以考虑状压DP。 * 用1表示存在从(1,1)到当前位置的路径,0则表示不存在。 * 假设前i-1行的状态pre,当前行i的状态now, * 那么就可以转移出前i行与(1,1)的联通状态。 * 因为最多只能放k个,所以用f[i][j][k]表示前i行, * 放置j个障碍后,与(1,1)联通状态为k的方案数。 * ans=∑f[n][1~k][st],其中st&2^(m-1)==0(即(n,m)与(1,1)不联通) * #include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 260#define MOD 1000000007int n,m,k;int s[N];int g[N][N];int f[10][70][N];int calc(int pre,int now){ int i,j,res=pre&now; for(i=0;i<m;++i) if((pre&(1<<i)) && (now&(1<<i))){ for(j=i+1;j< m && (now&(1<<j))>0;++j) res|=(1<<j); } return res;}void init(){ int i,j; for(i=0;i<(1<<m);++i){ s[i]=0; for(j=0;j<m;++j) if(!(i&(1<<j))){ ++s[i]; } } for(i=0;i<(1<<m);++i) for(j=0;j<(1<<m);++j){ g[i][j]=calc(i,j); }}int main(){ int T,tt=0; int i,j,pre,now,sta; scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&k); memset(f,0,sizeof(f)); init(); f[0][0][1]=1; for(i=1;i<=n;++i){ for(now=0;now<(1<<m);++now) for(j=s[now];j<=k;++j){ for(pre=0;pre<(1<<m);++pre){ sta=g[pre][now]; f[i][j][sta]+=f[i-1][j-s[now]][pre]; if(f[i][j][sta]>=MOD) f[i][j][sta]-=MOD; } } } int ans=0; for(i=1;i<=k;++i) for(j=0;j<(1<<m);++j) if(!(j&(1<<(m-1)))){ ans+=f[n][i][j]; if(ans>=MOD) ans-=MOD; } printf("Case #%d: %d\n",++tt,ans); } return 0;}
J
K
/* * DP、取模 * 题意: * 从N*M(N,M<=10^6)的(1,1)向右和向下走到(N,M)的路径数,其中某些3*3的格子不能走(3*3的矩阵<=10),结果数对997取模。 * 思路: * 当没有障碍的时候,方案数为C(N+M-2,N-1)(即总共走N-1+M-1步,其中N-1步向下走) * 假设只有1个障碍,那么就要从原方案中去掉包含该障碍的方案数,记该障碍的坐标为(x,y), * 则不合法的方案为C(x-1+y-1,x-1)*C(N-x+M-y,N-x) * 令f[i]表示从(1,1)出发到(x[i],y[i])的合法路径数, * 则f[j]=C(x[j]-1+y[j]-1,x[j]-1)-∑C(x[j]-x[i]+y[j]-y[i],x[j]-x[i]),其中x[i]<=x[j]&&y[i]<=y[j] * 结果需要对997取模,对于组合数C(n,m)=n!/(m!(n-m)!),通过逆元和快速幂得到。 * 但是997可能比N、M小,导致比997大的阶乘均为0。 * 这样导致的问题是某些组合数不包含997,但是由于阶乘均为0,导致结果就为0了。 * 所以对于阶乘的预处理中要去掉997,另开数组记录997的指数。 * 问题: * 做的时候DP作法对了,但是没有意识到997导致的问题。 */#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 107#define M 2000007#define MOD 997int dx[]={-1,-1,-1,0,1,1,1,0};int dy[]={-1,0,1,1,1,0,-1,-1};int f[N];int P[M],G[M];struct Node{ int x,y; bool operator<(const Node &p) const{ return x<p.x || (x==p.x && y<p.y); }} a[N];void init(){ int i,j; P[0]=1;G[0]=0; for(i=1;i<M;++i){ G[i]=G[i-1]; j=i; while(j%MOD==0){ ++G[i]; j/=MOD; } P[i]=(j%MOD*P[i-1])%MOD; }}int pow(int a,int b){ a%=MOD; int res=1; while(b>0){ if(b&1) res=(res*a)%MOD; a=(a*a)%MOD; b>>=1; } return res;}int work(int x,int y){ int res=P[x+y]*pow(P[x]*P[y],MOD-2)%MOD; res=(res*pow(MOD,G[x+y]-G[x]-G[y]))%MOD; return res;}void add(int &t,int x,int y){ a[t].x=x,a[t].y=y,++t;}int main(){ int T,tt=0; int n,m,k,i,j,t,r,c; init(); scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&k); t=0; add(t,1,1); add(t,n,m); for(i=0;i<k;++i){ scanf("%d%d",&r,&c); for(j=0;j<8;++j){ add(t,r+dx[j],c+dy[j]); } } sort(a,a+t); f[0]=1; for(i=1;i<t;++i){ f[i]=work(a[i].x-1,a[i].y-1); for(j=1;j<i;++j) if(a[j].x<=a[i].x && a[j].y<=a[i].y){ f[i]=(f[i]+MOD-f[j]*work(a[i].x-a[j].x,a[i].y-a[j].y)%MOD)%MOD; } } printf("Case #%d: %d\n",++tt,f[t-1]); } return 0;}
- UESTC 2016 Summer Training #19 Div.2(未完待续)
- UESTC 2016 Summer Training #2 Div.2(未完待续)
- UESTC 2016 Summer Training #1 Div.2(未完待续)
- UESTC 2016 Summer Training #3 Div.2(未完待续)
- UESTC 2016 Summer Training #4 Div.2(未完待续)
- UESTC 2016 Summer Training #5 Div.2(未完待续)
- UESTC 2016 Summer Training #6 Div.2(未完待续)
- UESTC 2016 Summer Training #10 Div.2(未完待续)
- UESTC 2016 Summer Training #18 Div.2(未完待续)
- UESTC 2017 Summer Training #2 Div.2
- UESTC 2017 Summer Training #1 Div.2
- UESTC 2016 Summer Training #2 Div.2 A dp、递推、多阶段问题
- UESTC 2016 Summer Training #1 Div.2 E - Accepted Passwords 讨论
- UESTC 2016 Summer Training #1 Div.2 F - Mission in Amman (A) 动态维护(刷新:--、++)
- UESTC 2016 Summer Training #1 Div.2 L - Plus or Minus (A) dfs
- UESTC 2016 Summer Training #1 Div.2 H - Queue (A) 贪心
- UESTC 2016 Summer Training #4 Div.2 A - (。•_•。) 预处理打表
- UESTC 2016 Summer Training #4 Div.2 B - ฅ(*`ω´*)ฅ 有趣的思维题
- 图结构练习——BFS——从起始点到目标点的最短步数
- Glide介绍及源码解析
- 关于speex使用(入门笔记)
- 官方API指南之提供资源
- linux根文件系统FHS
- UESTC 2016 Summer Training #19 Div.2(未完待续)
- 【第一章:dojo】5)多继承
- 杭电ACM2002与2003
- version `GLIBCXX_3.4.14' not found, 及shared_ptr.h error: '_Lock_policy' has not been declared 问题
- objective-c之内存管理
- uva10003+uva765+uva111
- java synchronized关键字的用法
- PHP 反向排序和随机排序代码函数
- 【C#】一个简单的windows服务程序