NOIP 2017 模拟
10 24
T1:
题目:
——正解思路:
首先我们用tarjan求出图中的所有的双连通分量,然后我们把每个分量看成一个点,就得到了一棵树。
使这棵树变成一个边双连通的,需要添加的边树 = ( 叶子数目 + 1 ) / 2。
——我的乱搞:
40 % 的求入度暴力,
剩下的嘛,,,,,感受那种知道该缩点却不会缩的痛苦。。。。
tips:
这个,,,我也没有办法啊,,,,看来还是我的暴力水平太蒟蒻了。。。。。
good good study, day day up.
等等。。。这不是T1嚒???怎么就上双连通分量了??!!
凯爷 :我昨天预告过了呀~~~~~~~~~~~~~
来来来,满载我们悲伤的正解代码:
#pragma GCC optimize("O3")
#include <fstream>
int n, m, x, y, cnt, idx, top, num, tot = 1;
int to[400040], next[400040], first[100010], dfn[100010], stk[100010], id[100010], du[100010], low[100010];
inline void read(int &x){
x = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
}
inline void add(int x, int y){
to[++tot] = y;
next[tot] = first[x];
first[x] = tot;
}
void tarjan(int u, int f){
dfn[stk[++top] = u] = low[u] = ++idx;
for(int i = first[u]; i; i = next[i]) if(i != (f ^ 1))
if(!dfn[to[i]]){
tarjan (to[i], i);
low[u] = std::min (low[u], low[to[i]]);
} else low[u] = std::min (low[u], dfn[to[i]]);
if(low[u] == dfn[u]){
++num;
while (stk[top] != u) id[stk[top--]] = num;
id[u] = num;
--top;
}
}
int main(){
read(n);
read(m);
for(int i = 1; i <= m; i++){
read(x);
read(y);
add(x, y);
add(y, x);
}
tarjan(1, 0);
if(!num){
puts("0");
return 0;
}
for(int i = 1; i <= n; i++)
for(int j = first[i]; j; j = next[j])
if(id[i] != id[to[j]]) ++du[id[to[j]]];
for(int i = 1; i <= num; i++) if(du[i] == 1) ++cnt;
printf("%d\n", (cnt + 1) >> 1);
return 0;
}
uses math;
var
n, m, x, y, i, j, cnt, idx, tot, top, num : longint;
too, nxt : array [0..400040] of longint;
dfn, low, id, du, stk, first : array [0..100010] of longint;
procedure add (x, y : longint);
begin
inc (tot);
too[tot] := y;
nxt[tot] := first[x];
first[x] := tot;
end;
procedure tarjan (u, from : longint);
var i : longint;
begin
inc (idx);
inc (top);
dfn[u] := idx;
low[u] := idx;
stk[top] := u;
i := first[u];
while i > 0 do begin
if i <> (from xor 1) then begin
if dfn[too[i]] = 0 then begin
tarjan (too[i], i);
low[u] := min (low[u], low[too[i]]);
end
else low[u] := min (low[u], dfn[too[i]]);
end;
i := nxt[i];
end;
if dfn[u] = low[u] then begin
inc (num);
while stk[top] <> u do begin
id[stk[top]] := num;
dec (top);
end;
id[u] := num;
dec (top);
end;
end;
begin
read (n, m);
tot := 1;
for i := 1 to m do begin
read (x, y);
add (x, y);
add (y, x);
end;
tarjan (1, 0);
if num = 0 then begin
write ('0');
exit;
end;
for i := 1 to n do begin
j := first[i];
while j > 0 do begin
if id[i] <> id[too[j]] then
inc (du[id[too[j]]]);
j := nxt[j];
end;
end;
for i := 1 to num do
if du[i] = 1 then inc (cnt);
write ((cnt + 1) shr 1);
end.
T2:
题目:
——正解思路:
状压dp + 分组背包
每一个数只会含有一个比 sqrt( n ) 大的数,状态压缩到一个数 mask 当中,dp [ i ][ k ][ mask ] 表示前 i 个数选择
了 k 个数,现在含有 mask 中这些因数。
sqrt ( n ) 以内的质数只有 8 个
然后 dp [ i ][ j ][ mask ] 表示计算完了前 i 组数,选择了 j 个数字, mask 表示当前乘积含有比 sqrt ( n ) 小的质因
数的集合。
然后进行背包就好。
——我的乱搞:
状压,结果写炸了。。。。
事实证明样例是靠不住的。。。。
tips :
看来以后真的需要学习写一下对拍了,,,
虽然正解并不是很好懂,,,但暴力水平的确是有点蒟蒻了,,,向 bszx 和 cw 的 da lao 们低头。
代码:
#pragma GCC optimize("O3")
#include <vector>
#include <cstring>
#include <fstream>
#define func(a, b) (a) = (a) + (b) >= (mod) ? (a) + (b) - (mod) : (a) + (b)
using namespace std;
int T, n, k;
int a[502], s[502], f[502][257];
unsigned t[502];
const long long mod = 1e9 + 7;
const int p[8] = {2, 3, 5, 7, 11, 13, 17, 19};
vector <int> v[502];
inline void read (int &x) {
x = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
}
int main () {
read (T);
while (T--) {
read (n);
read (k);
memset (f, 0, sizeof(f));
memset (s, 0, sizeof(s));
for (int i = 1; i <= n; ++i) v[i].clear();
for (int i = 1; i <= n; ++i) {
a[i] = i;
for (int j = 0; j < 8; ++j)
(!(a[i] % (p[j] * p[j]))) ? s[i] = -1 : (!(a[i] % p[j])) ? (a[i] /= p[j], s[i] |= (1 << j)) : 0;
}
for (int i = 1; i <= n; ++i) if (~s[i]) a[i] == 1 ? v[i].push_back(i) : v[a[i]].push_back(i);
f[0][0] = 1;
for (int i = 1; i <= n; ++i)
if (~s[i] and v[i].size()) {
int sz = v[i].size();
for (int j = 0; j < sz; ++j) t[j] = s[v[i][j]];
for (int j = k - 1; j >= 0; --j)
for (int l = 0; l < sz; ++l)
for (int v = t[l], s = 255 ^ v; ; s = (s - 1) & (255 ^ v)) {
if (!(s & t[l])) func(f[j + 1][s | v], f[j][s]);
if (!s) break;
}
}
long long ans = 0;
for (int i = 1; i <= k; i++)
for (int j = 0; j < 256; j++)
ans += f[i][j];
printf ("%I64d\n", ans % mod);
}
return 0;
}
T3 :
题目:
——正解思路:
对于 a 为奇数的情况,b 一定为奇数,下证 b = a mod 2 ^ n。
由于奇数平方 mod 8 = 1, 故 a ^ b = a mod 8, b ^ a = b mod 8
故 a = b mod 8
由于奇数四次方 mod 16 = 1, 故 a ^ b = a ^ ( b % 4 ) mod 16 , b ^ a = b ^ ( a % 4 ) mod 16
由于 b % 4 = a % 4,故 a = b mod 16
以此类推,得 b = a mod 2 ^ n 。解唯一
对于 a 为偶数的情况,b 一定为偶数。
若 b >= n, 则 a ^ b = 0 mod 2 ^ n,故 b ^ a = 0 mod 2 ^ n。可以直接求出 b >= n 时的个数。
若 b < n,则直接暴力。
——我的乱搞:
智商透支,有时是在过度coding后,,腰腿酸痛,精神不振,好像暴力写不出。。。。。
向打表的 da lao 低头。。。。
tips :
世上还有打表找规律的操作,好好学习,day day up.
coding不仅只有眼前的暴力,还有 远方的暴力 和 分段的暴力 和 打表的暴力 和 猜答案的暴力 和 暴搜的暴力 和
骗样例的暴力 和 连出题人都万万没想到的风骚蛇皮走位暴力 和 自以为写的是正解却各种ce/re/tle/mle/wa/pe的
暴力。。。。。。
凯爷:
代码:
#pragma GCC optimize("O3")
#include <iostream>
using namespace std;
int t, a, n, ans;
inline int ksc (int a, int b, int p) {
int ans = 0;
while (b) {
if (b & 1) ans = (ans + a) % p;
b >>= 1;
a = (a << 1) % p;
}
return ans;
}
inline int ksm (int a, int b, int p) {
int ans = 1;
while (b) {
if (b & 1) ans = ksc (ans, a, p);
b >>= 1;
a = ksc (a, a, p);
}
return ans;
}
int main () {
cin>>t;
while (t--) {
cin>>a>>n;
if (a & 1) {
cout<<"1"<<endl;
continue;
}
ans = 0;
for (int i = 2; i <= n; i += 2)
if (ksm (a, i, 1 << n) == ksm (i, a, 1 << n))
++ans;
ans += ((1 << n) >> (n + a - 1) / a) - (n >> (n + a - 1) / a);
cout<<ans<<endl;
}
return 0;
}