總結——關於2017 10 24測試的分析總結

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值