SXOI2016 部分解题报告
第四题还没做…先贴前三个题的。
T1 bridge
Description
有一支
Input
- 第一行为两个正整数
W,n - 接下来
n 行每行两个数ti,wi 。
Output
- 仅一个数,为最小花费时间。
Sample
Input
100 3
24 60
10 40
18 50
Output
42
Hint
- 对于40%的数据,
n≤8 - 对于70%的数据,
n≤12 - 对于100%的数据,
n≤16,100≤W≤400,1≤t≤50,10≤w≤100
Solution
数据范围告诉我们应该考虑状压dp。
用
T(A),W(A)分别表示通过的时间最大值和重量总和。
现在问题转化为如何枚举集合的子集。如果用朴素的枚举只能拿到70分左右。从《挑战程序设计竞赛》上学到的一个方法则可以降低复杂度:
sub = S;
do {
//...
sub = (sub-1)&S;
} while (sub > 1)
这样处理sub就枚举了
总的复杂度可以用:
打个表就知道能O(能过)…
#include <bits/stdc++.h>
using namespace std;
int dp[1<<18], W, n;
int w[20], t[20];
int A[1<<18], M[1<<18];
int dfs(int S)
{
if (S == 1) return 0;
if (dp[S] != -1) return dp[S];
dp[S] = 1e6;
int sap = S;
do {
int ans = A[sap], mx = M[sap];
if (ans <= W)
dp[S] = min(dp[S], dfs(S^sap)+mx);
sap = S&(sap-1);
} while (sap > 1);
//cout << S << " --> " << dp[S] << endl;
return dp[S];
}
int main()
{
freopen("bridge.in", "r", stdin);
freopen("bridge.out", "w", stdout);
memset(dp, -1, sizeof dp);
scanf("%d%d", &W, &n);
for (int i = 1; i <= n; i++)
scanf("%d%d", &t[i], &w[i]);
int S0 = (1<<(n+1))-1;
for (int i = 2; i <= S0; i++) {
for (int j = 1; j <= n; j++)
if (i&(1<<j))
A[i] += w[j], M[i] = max(M[i], t[j]);
}
cout << dfs(S0) << endl;
return 0;
}
T2 sequence
Description
定义
而
对于给定的
Input
- 一个数n
Output
- 一个数
Gnmod109+7 。
Sample
Input
3
Output
5
Hint
- 对于30%的数据,
n≤20 - 对于50%的数据,
n≤5000 - 对于70%的数据,
n≤107 - 对于100%的数据,
n≤1018
Solution
首先看样例解释很容易发现一个递推式:
打表找规律会发现
因此:
原命题得证。然后只需要矩阵
做快速幂取模就好了。注意特判。
#include <bits/stdc++.h>
using namespace std;
struct Matrix {
long long a[3][3];
Matrix()
{ memset(a, 0, sizeof a); }
friend Matrix operator * (const Matrix &a, const Matrix &b)
{
Matrix C;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
for (int k = 1; k <= 2; k++)
(C.a[i][j] += a.a[i][k] * b.a[k][j]) %= 1000000007;
return C;
}
};
Matrix I()
{
Matrix C;
memset(C.a, 0, sizeof C.a);
C.a[1][1] = C.a[2][2] = 1;
return C;
}
Matrix power(const Matrix &a, long long n)
{
if (n == 0) return I();
Matrix p = power(a, n>>1);
p = p*p;
if (n&1) p = p*a;
return p;
}
int main()
{
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
long long n;
cin >> n;
if (n == 0) {cout << 1 << endl; return 0;}
if (n == 1) {cout << 1 << endl; return 0;}
Matrix M; M.a[1][1] = 2, M.a[1][2] = 1, M.a[2][1] = 1;
M = power(M, n-1);
cout << M.a[1][1] << endl;
return 0;
}
T3 string
Description
给定
Input
- 第一行一个整数
n - 接下来
n 行,每行先是一个整数w ,表示这个字符串的长度;然后是一个空格;之后给出这个长度为w 的字符串。保证w≠0 。
Output
- 仅一个正整数,表示有序对的个数。
Sample
Input
7
2 aa
3 aba
3 aaa
6 abaaba
5 aaaaa
4 abba
Output
14
Hint
- 14个有序对中,6个为
(i,i) ,其他8个分别为: (1,3),(1,5),(3,5),(2,6),(3,1),(5,1),(5,3),(6,2) m 为总字符数。- 对于20%的数据,
n≤100,m≤500 - 对于40%的数据,
n≤5000,m≤2000000 - 对于100%的数据,
n≤2000000,m≤2000000
Solution
当时考场上sdf一个女选手怒而碾过Orz……
我们用
考虑两个回文串
满足题目中条件为:
按照穿脱原则展开:
也就是:
所以原问题转化为了求
移项并整理,得到:
可以看到两侧都变成了只含有一个量的表达式,成功地将两个量的关系转化成了一个量的性质,除法可以用模大素数下的乘法逆元处理,因此就可以丢进hash算了。
然而一个问题是这样做冲突严重。如果懒得写拉链怎么办呢?一个有趣的方法是考虑
至于base的选取..亲测37效果最好…不知道为什么,可能是数据的缘故吧…
#include <bits/stdc++.h>
using namespace std;
long long mod = 2016122203ll, base = 37;
long long power(long long a, long long n)
{
if (n == 0) return 1;
long long p = power(a, n>>1);
(p *= p) %= mod;
if (n&1) (p *= a) %= mod;
return p;
}
long long inv(long long a)
{ return power(a, mod-2); }
long long hash_val(char str[])
{
long long ans = 0;
for (char *p = str; *p != '\0'; ++p)
((ans *= base) += (*p-'a'+1)) %= mod;
return ans;
}
map<long long, int> hash_set;
int n, w;
char str[2000005];
long long cnt = 0;
inline int read() {
int a = 0, c;
do c = getchar(); while(!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
inline void read(char str[])
{
char c;
int i = 0;
do c = getchar(); while(!isalpha(c));
while (isalpha(c)) {
str[i++] = c;
c = getchar();
}
str[i] = '\0';
}
int main()
{
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
w = read();
read(str);
long long val = inv(((power(base, w)-1)+mod)%mod);
(val *= hash_val(str)) %= mod;
cnt += hash_set[val*str[w-1]];
hash_set[val*str[w-1]]++;
}
cout << cnt*2+n << endl;
return 0;
}