单挑养成计划【2】 AtCoder Grand Contest 005

ABC

我懒得写了。。(啪

 

D - ~K Perm Counting

题意:求有多少种1~N的全排列,不存在位置i,使得i上的数ai满足 |ai - i| = K。

题解:一眼容斥。思考怎么算有i个不合法位置的方案数Mi。考虑转化成一个二分图,左边N个数代表位置,右边N个数代表数字。在图上建边跑DP。有很多种姿势的DP啊非常迷……边边点点的搞一搞(

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <vector>
 6 #include <algorithm>
 7 using namespace std;
 8 typedef long long ll;
 9 #define rp(i,a,b) for(int (i)=(int)(a);i<(int)(b);++i)
10 ll dp[4020][2020][2];
11 ll mod=924844033;
12 int main(){
13     int N,K;
14     cin>>N>>K;
15     vector<ll> m;
16     ll c=0,s=0;
17     bool t[2001];
18     for(int i=1;i<N+1;i++){
19         if(!t[i]){
20             s+=(N-i)/K*2;
21             rp(j,0,(N-i)/K) m.push_back(c);
22             c++;
23             rp(j,0,(N-i)/K) m.push_back(c);
24             c++;
25             for(int j=i;j<=N;j+=K){
26                 t[j]=true;
27             }
28         }
29     }
30     dp[0][0][0]=1;
31     rp(i,0,s){
32         rp(j,0,min(ll(2001),ll(i+1))){
33             (dp[i+1][j+1][1]+=dp[i][j][0])%=mod;
34             if(m[i]!=m[i-1]) (dp[i+1][j+1][1]+=dp[i][j][1])%=mod;
35             (dp[i+1][j][0]+=(dp[i][j][0]+dp[i][j][1]))%=mod;
36         }
37     }
38     ll per[2001];
39     per[0]=1;
40     rp(i,1,2001){
41         per[i]=(per[i-1]*i)%mod;
42     }
43     ll sub=0;
44     for(int i=1; i<=min(ll(s),ll(N)); ++i)
45     {
46         if(i%2==0) sub = (sub - ((dp[s][i][0]+dp[s][i][1])*per[N-i])%mod + mod)%mod;
47         else sub = (sub + ((dp[s][i][0]+dp[s][i][1])*per[N-i])%mod)%mod;
48     }
49     
50     ll ans=(per[N]-sub+mod)%mod;
51     cout << ans << endl;
52     return 0;
53     
54 };
View Code

 

E - Sugigma: The Showdown

题意:两个人在一张图上做游戏,A有一个红色的生成树,B有一个蓝色的生成树,起点分别在X,Y号点,两个人轮流走,每次只能沿自己对应的颜色走一条边或者不动弹。问A最多能拖多少步才能让B追上。

题解:我们很容易发现,A是足够安全的当且仅当有两个点u,v满足 Lred(u,v) = 1 且 Lblue(u,v) >= 3,并且A站在u,v之中的一点上。那么问题就转化成了A能不能安全的跑到这些安全点上。因为两棵树放在一起不好处理,我们选择固定B的那一棵不动,A的行为就可以表示为在蓝色的树上跳来跳去。并且由于A当前不在安全点上,所以跳一步的距离不会超过2:这样A就只能始终在以B当前点为根的子树上,不会跳回去自寻死路。到这里问题就简单啦,因为很容易知道当前点的深度(B追到这里的步数),A无法占据安全点的话,坚持到尽可能深的点上被抓就好啦。

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <vector>
 7 #include <queue>
 8 using namespace std;
 9 const int maxn = 2e6 + 5;
10 vector<int> blue[maxn], red[maxn];
11 int dep[maxn], fa[maxn], dp[maxn];
12 bool safe[maxn], vis[maxn];
13 void dfs(int v, int p) {
14     fa[v] = p;
15     for (int i=0; i<blue[v].size(); i++) if(blue[v][i] != p) {
16         dep[blue[v][i]] = dep[v] + 1;
17         dfs(blue[v][i], v);
18     }
19 }
20 bool atMost2(int x, int y) {
21     return (fa[x] == fa[y]) || (fa[x] == y) || (x == fa[y]) || (fa[fa[x]] == y) || (x == fa[fa[y]]);
22 }
23 int main() {
24     int n, X, Y, x, y;
25     scanf("%d %d %d", &n, &X, &Y);
26     for (int i=1; i<n; i++) {
27         scanf("%d %d", &x, &y);
28         red[x].push_back(y), red[y].push_back(x);
29     }
30     for (int i=1; i<n; i++) {
31         scanf("%d %d", &x, &y);
32         blue[x].push_back(y), blue[y].push_back(x);
33     }
34     dfs(Y, -1);
35     for (int i=1; i<=n; i++)
36         for (int j=0; j<red[i].size(); j++)
37             if (!atMost2(i, red[i][j]))
38                 safe[i] = safe[red[i][j]] = 1;
39     if (safe[X]) {puts("-1"); return 0;}
40     memset(dp, -1, sizeof(dp));
41     queue<int> q;
42     q.push(X); dp[X] = 0; vis[X] = 0;
43     int ans = 0;
44     while (!q.empty()) {
45         int u = q.front(); q.pop();
46         if (dp[u] >= dep[u]) continue;
47         if (safe[u]) {puts("-1"); return 0;}
48         ans = max(ans, dep[u] * 2);
49         for (int i=0; i<red[u].size(); i++) {
50             int v = red[u][i];
51             if (!vis[v] && dp[u] + 1 < dep[v] && atMost2(u, v)) {
52                 dp[v] = dp[u] + 1;
53                 vis[v] = 1;
54                 q.push(v);
55             }
56         }
57     }
58     printf("%d\n", ans);
59     return 0;
60 }
View Code

 

F。。等我学会了FFT再说吧(

posted on 2016-10-26 19:54  HoneyCat  阅读(260)  评论(0编辑  收藏  举报

导航