Codeforces Round #369 (Div. 2)
C. Coloring Trees
O(n^4)暴力DP就好了
#include <iostream> #include <algorithm> #include <cstdio> #include <math.h> #include <set> #include <map> #include <queue> #include <string> #include <string.h> #include <bitset> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) #define hr putchar(10) #define pb push_back #define lc (o<<1) #define rc (lc|1) #define mid ((l+r)>>1) #define ls lc,l,mid #define rs rc,mid+1,r #define x first #define y second #define io std::ios::sync_with_stdio(false) #define endl '\n' using namespace std; typedef long long ll; typedef pair<int,int> pii; const int P = 1e9+7; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;} ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;} //head const ll INF = 0x3f3f3f3f3f3f3f3f; int n, m, k; ll dp[111][111][111]; int c[111][111], a[111]; void chkmin(ll &x, ll y) {x=min(x,y);} int main() { scanf("%d%d%d", &n, &m, &k); if (m==1&&k>1) return puts("-1"); REP(i,1,n) scanf("%d", a+i); REP(i,1,n) REP(j,1,m) scanf("%d", &c[i][j]); memset(dp,0x3f,sizeof dp); dp[0][1][1] = 0; REP(i,1,n) REP(j,1,k) REP(pre,1,m) { int nxt; if (a[i]) { if (i==1) nxt=1; else nxt=j+(pre!=a[i]); chkmin(dp[i][nxt][a[i]],dp[i-1][j][pre]); continue; } REP(now,1,m) { if (i==1) nxt=1; else nxt=j+(pre!=now); chkmin(dp[i][nxt][now],dp[i-1][j][pre]+c[i][now]); } } ll ans = INF; REP(j,1,m) chkmin(ans,dp[n][k][j]); printf("%lld\n", ans==INF?-1:ans); }
D. Directed Roads
大意: n个点的有向图, 每个点出度为1, 每条边的方向可以改变, 问有多少种方案使得图无环.
每个点出度为1, 那么图是一个基环树森林. 再观察一下可以发现每个连通块的答案为$2^n-2^{n-环上点数+1}$
#include <iostream> #include <algorithm> #include <cstdio> #include <math.h> #include <set> #include <map> #include <queue> #include <string> #include <string.h> #include <bitset> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) #define hr putchar(10) #define pb push_back #define lc (o<<1) #define rc (lc|1) #define mid ((l+r)>>1) #define ls lc,l,mid #define rs rc,mid+1,r #define x first #define y second #define io std::ios::sync_with_stdio(false) #define endl '\n' using namespace std; typedef long long ll; typedef pair<int,int> pii; const int P = 1e9+7, INF = 0x3f3f3f3f; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;} ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;} //head const int N = 1e6+10; int n, ans, sz, tot; int dfn[N], fa[N]; vector<int> g[N]; void dfs(int x) { dfn[x] = ++*dfn,++tot; for (int y:g[x]) { if (dfn[y]) { if (dfn[y]<dfn[x]) continue; for (; y!=x; y=fa[y]) ++sz; } else fa[y]=x, dfs(y); } } int main() { scanf("%d", &n); REP(i,1,n) { int t; scanf("%d", &t); g[t].pb(i); g[i].pb(t); } int ans = 1; REP(i,1,n) if (!dfn[i]) { tot=sz=0,dfs(i); ll t = qpow(2,tot)-qpow(2,tot-sz); ans = ans*t%P; } if (ans<0) ans+=P; printf("%d\n", ans); }
E. ZS and The Birthday Paradox
大意: 假设一年有$2^n$天,问$k$个小朋友中有两个小朋友生日相同的概率
显然答案为$1-\frac{2^n}{2^n}\frac{2^n-1}{2^n}\cdots\frac{2^n-k+1}{2^n}$
因为模数比较小, 分子可以暴力求出. gcd显然为2的幂, 统计一下再除去gcd即可
#include <iostream> #include <algorithm> #include <cstdio> #include <math.h> #include <set> #include <map> #include <queue> #include <string> #include <string.h> #include <bitset> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) #define hr putchar(10) #define pb push_back #define lc (o<<1) #define rc (lc|1) #define mid ((l+r)>>1) #define ls lc,l,mid #define rs rc,mid+1,r #define x first #define y second #define io std::ios::sync_with_stdio(false) #define endl '\n' using namespace std; typedef long long ll; typedef pair<int,int> pii; const int P = 1e6+3, INF = 0x3f3f3f3f; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;} ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;} //head ll n, k; int main() { scanf("%lld%lld", &n, &k); if (n<=61&&(1ll<<n)<k) return puts("1 1"),0; ll cnt = n%(P-1); for (ll i=(k-1)>>1; i; i>>=1) (cnt+=i)%=(P-1); cnt = P-1-cnt; cnt = qpow(2,cnt); ll x = qpow(2,n), A=1, B = qpow(x,k); if (k-1>=x) A = 0; else REP(i,0,k-1) A=A*(x-i)%P; A = A*cnt%P, B = B*cnt%P; A = (B-A)%P; if (A<0) A+=P; printf("%lld %lld\n",A,B); }