P 2016 战略游戏
先喂一波题意:给一颗树,选最小点覆盖(选定一个点,与它相连的那些点也将被覆盖)
1. 树形dp
#include <cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1505;
int n,cur;
//树形dp
int dp[1505][4];
//链式前向星存边
int to[maxn*2],nxt[maxn*2],head[maxn];
void add(int x,int y){
to[++cur]=y;
nxt[cur]=head[x];
head[x]=cur;
}
void dfs(int u,int fa){
dp[u][0]=0;dp[u][1]=1;
for(int i=head[u];i;i=nxt[i]){
if(to[i]==fa) continue;
dfs(to[i],u);
dp[u][0]+=dp[to[i]][1];
dp[u][1]+=min(dp[to[i]][0],dp[to[i]][1]);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int id,k;
cin>>id>>k;
for(int j=1;j<=k;j++){
int a;
cin>>a;
add(id,a);
add(a,id);
}
}
dfs(0,-1);
cout<<min(dp[0][0],dp[0][1])<<endl;
return 0;
}
2. 贪心
策略 如下:从叶子开始处理,对每一个正在处理的节点,查看它是否被点亮,如果未被点亮,点亮它的父亲(点父亲总不会比点亮自己差,父亲可能连接更多的点,如果已经被点亮,就暂时不需点亮它的父亲(不一定需要),再看它父亲的其他节点;当发现它父亲的儿子已经全被点亮而它父亲还未被点亮,将父亲放入队列后续判断。
最后统计点亮个数即可。
#include <cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=1505;
int n,cur,root,c[maxn],fa[maxn],ans;
bool li[maxn];
vector<int> v[maxn];
queue<int> q;
int main(){
cin>>n;
for(int i=0;i<n;i++) fa[i]=-1;
for(int i=1;i<=n;i++){
int id,k;
cin>>id>>k;
c[id]+=k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
fa[x]=id;
v[id].push_back(x);
}
}
for(int i=0;i<n;i++){
if(v[i].size()==0){
q.push(i);
}
if(fa[i]==-1) root=i;
}
//n=1要特判一下 因为n=1进不了循环
if(n==1) {
cout << 1<<endl;
return 0;
}
while(q.front()!= root){
int now=q.front();
q.pop();
c[fa[now]]--;
if(!li[now]) li[fa[now]]=1;
if(!c[fa[now]]) q.push(fa[now]);
}
for(int i=0;i<n;i++)
if(li[i]) ans ++ ;
cout << ans<<endl;
return 0;
}
3 匈牙利算法
小结论:
最小覆盖数==最大匹配数(如果是无向图 == 最大匹配数/2)
#include <cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=7000;
int n,ans,cnt,head[maxn],to[maxn*2],match[maxn*2],nxt[maxn*2];
bool vis[maxn];
void add(int x,int y){
to[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
bool dfs(int x){
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(!vis[y]){
vis[y]=1;
if(match[y]==-1|| dfs(match[y])){
match[y]=x;
return 1;
}
}
}
return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int id,k;
cin>>id>>k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
add(x,id);
add(id,x);
}
}
for(int i=0;i<n;i++) match[i]=-1;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) vis[j]=0;
if(dfs(i)) ans ++;
}
cout << ans/2 <<endl;
return 0;
}
P1525
两种做法-
1. 并查集
并查集带权,实现基于贪心的思想。把冲突值递减排列,尽量把值大的两个人划分到不同的监狱。有一个enemy数组,用来存放一个人的敌人团队。
如果冲突,就直接输出,因为已经按照最优策略排列。
// ConsoleApplication5.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
using namespace std;
const int maxn = 1e5 + 5;
int N, M, con[maxn],enemy[maxn],f[maxn];
struct R {
int u, v, w;
bool operator <(const R r) {
return w > r.w;
}
}r[maxn];
void ini() {
for (int i = 1;i <= N;i++) {
f[i] = i;
}
}
//这里都是板子
int find(int x) {
return x == f[x] ? x : f[x]=find(f[x]);
}
void merge(int x, int y) {
f[find(x)] = f[find(y)];
return;
}
int main() {
cin >> N >> M;
for (int i = 1;i <= M;i++) {
cin >> r[i].u >> r[i].v >> r[i].w;
}
ini();
sort(r + 1, r + 1 + M);
for (int i = 1;i <= M;i++) {
//cout << r[i].u << " " << r[i].v << " " << r[i].w << endl;
int p1 = find(r[i].u);
int p2 = find(r[i].v);
//如果这两个人的阵营被安排了,直接输出
if (p1 == p2) {
cout << r[i].w << endl;
return 0;
}
//设置不同阵营
if (!enemy[r[i].u]) {
enemy[r[i].u] = r[i].v;
}
else {
merge(enemy[r[i].u], r[i].v);
}
if (!enemy[r[i].v]) {
enemy[r[i].v] = r[i].u;
}
else {
merge(enemy[r[i].v], r[i].u);
}
}
cout << 0 << endl;
return 0;
}
2 二分图
个人感觉二分图比较好想到,二分答案
教训(L+1<R)不可以乱写,比如这种L<= R就会在检查[x,x]这样的区间的时候GG
// ConsoleApplication5.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 1e5 + 5;
int N, M, R;
struct P {
int nxt, to, w;
bool operator <(const P r) {
return w > r.w;
}
};
int head[maxn*2],cnt;
P edge[maxn * 2];
void addedge(int u, int v, int w) {
edge[++cnt].w = w;
edge[cnt].nxt = head[u];
edge[cnt].to = v;
head[u] = cnt;
}
//搭个架子先,这里是二分染色咯
bool test(int x) {
queue <int> q;
//标记颜色
int color[maxn*2] = {};
for (int i = 1;i <= N;i++) {
if (!color[i]) {
q.push(i);
color[i] = 1;
while (!q.empty()) {
int pp = q.front();
q.pop();
for (int j = head[pp];j;j = edge[j].nxt){
int qq = edge[j].to;
if (edge[j].w >= x) {
if (!color[qq]) {
q.push(qq);
if (color[pp] == 1)
color[qq] = 2;
else color[qq] = 1;
}
else if (color[pp] == color[qq])
return false;
}
}
}
}
}
return true;
}
int main() {
cin >> N >> M;
for (int i = 1;i <= M;i++) {
int u, v, w;
cin >> u >> v >> w;
addedge(u, v, w);
addedge(v, u, w);
R = max(R, w);
}
int L = 0;
R++;
//R++???????????
while (L +1 < R) {
int mid = (L+R)>>1;
//如果可以,缩小范围
if (test(mid)) R = mid;
//不行,扩大
else L = mid;
}
cout << L << endl;
return 0;
}
P1113 是个水题 但还是成功掉坑两次
#include "pch.h"
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 1e4 + 5;
int n,degree[maxn],ans,l[maxn],tim[maxn];
vector<int> v[maxn];
void topo() {
queue<int> q;
for (int i = 1;i <= n;i++) {
if (degree[i] == 0) {
q.push(i);
tim[i] = l[i];
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0;i < v[u].size();i++) {
int nxt = v[u][i];
degree[nxt]--;
if (degree[nxt] == 0) {
q.push(nxt);
}
tim[nxt] = max(tim[u] + l[nxt], tim[nxt]);
}
}
for (int i = 1;i <= n;i++)
ans = max(ans, tim[i]);
}
int main() {
cin >> n;
for (int i = 1;i <= n;i++) {
int id, x,len;
cin >> id >> len;
l[id] = len;
while (cin >> x && x) {
degree[id]++;
v[x].push_back(id);
}
}
topo();
cout << ans << endl;
return 0;
}
P1983 实在是妙题
TOPO
从未经过车站向经过车站连边建图,然后topo一下
(说实话是第一次写拓扑排序呢,离散学得很垃圾…
描述一下流程,免得以后忘记了
(1)队列操作
(2)将入度0的点入队,查找与这个点相连的其他点,让他们的入度–,若入度为0,入队,更新层次个数(是这题目里要维护的最大值),直到队列为空,结束。
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 1e5 + 5;
//topo排序
int degree[maxn],n,m,ans;
bool vis[maxn];
vector<int> v[maxn];
struct P {
int id, cnt, book[1005];
}s[1005];
bool edge[1005][1005];
void topo() {
queue<pair<int, int> > q;
for (int i = 1;i <= n;i++) {
if (degree[i] == 0) {
q.push(make_pair(i,1));
}
}
ans = 1;
while (!q.empty()) {
int u = q.front().first, val =q.front().second ;
q.pop();
for (int i = 0;i < v[u].size();i++) {
int nxt = v[u][i];
// cout << "Pass by " << nxt << endl;
degree[nxt]--;
if (degree[nxt] == 0) {
q.push(make_pair(nxt, val + 1));
// cout << "val + 1 = " << val + 1<< endl;
ans = max(ans, val + 1);
}
}
}
}
int tmp[maxn];
void ini() {
for (int i = 1;i <= n;i++)
vis[i] = false;
}
int main() {
cin >> n >> m;
for (int i = 1;i <= m;i++) {
ini();
cin >> s[i].cnt;
for (int j = 1;j <= s[i].cnt;j++) {
//第i列火车的第j个停靠点
int x; cin >> x;
s[i].book[j] = x;
//站点标记,代表这是此次列车的一个停靠点
vis[x] = true;
}
//此次经过的所有站点(始发站 -> 终点站)
for (int j = s[i].book[1];j <= s[i].book[s[i].cnt];j++) {
//若无停靠,入为0
if (vis[j]) continue;
for (int k = 1;k <= s[i].cnt;k++) {
//如果边标记过了,就无需标记,所以这个edge数组是不需要每次清空的
//因为多趟列车,所以会有重复的
if (!edge[j][s[i].book[k]]) {
degree[s[i].book[k]]++;
v[j].push_back(s[i].book[k]);
edge[j][s[i].book[k]] = 1;
}
}
}
}
topo();
cout << ans << endl;
return 0;
}
更新于2020.3.11
今天在洛谷玩耍 看到noip的初试题(没错就是高中同学说不满分就不能出线)
不过学会了一个单调栈求大于某个数的第一个位置(lower bound?
)类似吧
思想是这样的,把第一个数字装入栈中。
每次取出顶上的元素,与下一个元素比较,有两种情况:
若栈顶元素小于下一个元素,那么下一个元素就是大于栈顶元素的第一个元素,为栈顶元素更新答案,出栈(直到栈中的元素大于下一个元素或者栈为空),让下一个元素进栈;
若栈顶元素大于下一个元素,把下一个元素也入栈,这样继续查找,找到的答案一定先满足栈顶元素,再符合栈底元素(因为栈底比栈顶大嘛)。
并查集经典好题 食物链
真的好爱洛谷dalao讲解真的好清楚 很棒
//i
#include"pch.h"
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#define pi pcos(-1.0)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mp make_pair
using namespace std;
const int maxn = 3e5 + 5;
int n, k,f[maxn],ans;
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
int main() {
cin >> n >> k;
for (int i = 1;i <= 3*n;i++) {
f[i] = i;
}
for (int i = 1;i <= k;i++) {
int flag, u, v; cin >> flag >> u >> v;
if (u > n || v > n ) { ans++;continue; }
if (flag == 1) {
//同类 如果存在吃与被吃的关系,假
if (find(u + n) == find(v) || find(u) == find(v + n))
ans++;
else {
f[find(u)] = find(v);
f[find(u + n)] = find(v + n);
f[find(u + n + n)] = find(v + n + n);
}
}
else {
//u吃v,如果存在反吃关系或同类关系,假
if (find(v) == find(u+n) || find(u) == find(v))
ans++;
else {
f[find(v + n)] = find(u);
f[find(v + n + n)] = find(u + n);
f[find(v)] = find(u + n + n);
}
}
}
cout << ans << endl;
return 0;
}
P1017 进制转换
负进制数的使用
推导:设n/m=n’
则模(余数,也就是ans中的数)n-n’*m = j
与正数不同,负数%负数=负数,因此我们需要把负数转化为正数,同时不影响答案so
j-=m(m是负数)
那么左侧 n-n’*m - m = j-m
即为n-(n’+1)*m = j-m(正数)
因此,在n/m后,还需要加1
代码中先n+=m
达到一样的效果
//快读没有使用到
#define ll long long
#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
int n,r;
inline int read(void)
{
register int x = 0, c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
string ans="";
int main() {
cin >> n >> r;
int t = n;
while (n) {
int add = (n%r);
if (add < 0)
{
add -= r; n += r;
}
if (add <= 9)ans += char(add + '0');
else ans += char(add - 10 + 'A');
n /= r;
}
cout << t << "=";
for (int i = ans.length()-1;i >= 0;i--)cout << ans[i];
cout << "(base" << r << ")" << endl;
return 0;
}
P2789 统计直线交点数
虽然是简单题,但是也emmm
思想是归纳(?
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<map>
#include<string.h>
using namespace std;
const int maxn = 2e5+5;
bool f[maxn];
long long ans = 0;
void work(int m,int cur) {
//n条线,m条平行
//f[k]表示总交点数为k是否可行。
if (m == 0) {
if (f[cur] == 0) {
ans++;
f[cur] = 1;
}
return;
}
else {
for (int i = 1;i <= m;i++) {
work(m-i, cur + i * (m - i));
}
}
}
int main() {
int n;cin >> n;
work(n, 0);
cout << ans << endl;
return 0;
}
P1246 编码
是一道蛮细节的题,计算组合数的
注意特判是>=
#include"pch.h"
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<map>
#include<string.h>
#include<string>
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 2e6+5;
int ans = 0;
ll Cnm(ll n, ll m) {
//教训:Cnm也不能乱写,会四舍五入
if (m == 0)return 1;
if (2 * m > n)
m = n - m;
ll res = 1;
for (int i = 1;i <= m;i++) {
res = res * (n - m + i) / i;
}
return res;
}
void work(string s) {
int len = s.length();
for (int i = 0;i < len-1;i++) {
if (s[i] >= s[i + 1]) {
ans = 0;return;
}
}
//all 长度小于len的组合总数,l=i
for (int i = 1;i < len;i++) {
ans += Cnm(26, i);
}
//cout << ans << endl;
//前i位与s相同的len位字符
for (int i = 0;i < len;i++) {
//i位相同,还有剩余len-i位不同
//但排在s前,因此选择有限
//字母两两不重复,因此只能在未出现的26-i个字母中选择
int st = 1;if (i > 0)st = max(st, s[i - 1]-'a'+ 2);
for (int j = st;j <s[i]-'a'+1;j++) {
ans += Cnm( 26 - j, len - i-1);
//cout << "cnm: " << 26 - j << " " << len - i-1 << "= "<< Cnm(26 - j, len - i-1) <<endl;
// cout << ans << endl;
}
}
ans++;
return;
}
int main() {
//cout << Cnm(25, 2) << endl;
string s;cin >> s;
work(s);
cout << ans << endl;
return 0;
}
图论题目
P1127 词链
是有趣的题目!
以26个字母为点,单词为边,那么要求我们经过这个图的每一条边寻找一条路径,使得每条边(每个单词)都出现一次,这就是欧拉回路的定义(欧拉通路也可,欧拉通路是欧拉回路的特殊情况)。
判断欧拉回路
(1)判断无向图连通-连通用并查集
(连通块个数为1)
(2)欧拉回路中,
起点:出度=入度+1
终点:入度=出度+1
如果每个点的起点和终点相同,可以选择一个出现过的字母的点作为起点
(3)模拟答案:dfs,顺便开一个ans数组记录答案
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include<string.h>
#include <stack>
#include<cmath>
#include <iomanip>
#include<map>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 50;
int book[maxn], f[maxn];
int in[maxn], out[maxn];
bool vis[5000];
int head = -1;
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
vector<int> ve[5000];
string s[5000], ans[5000];
int ptr = 0,n;
void add(int x, int y) {
ve[x].push_back(y);
ve[y].push_back(x);
}
bool judge() {
for (int i = 0;i < 26;i++) f[i] = i;
for (int i = 0;i < 26;i++) {
for (int j = 0;j < ve[i].size();j++) {
int a = i;
int b = ve[i][j];
int fa = find(a), fb = find(b);
if (fa != fb) f[fa] = fb;
}
}
int cnt = 0;
for (int i = 0;i < 26;i++) {
if (book[i] && f[i] == i) {
cnt++;
}
}
return cnt == 1;
}
void dfs(int x) {
for (int i = 1;i <= n;i++) {
if (s[i][0] - 'a' == x && !vis[i]) {
vis[i] = 1;
dfs(s[i][s[i].length() - 1] - 'a');
ans[++ptr] = s[i];
}
}
}
int main() {
cin >> n;
for (int i = 1;i <= n;i++)cin >> s[i];
sort(s + 1, s + 1 + n);
for (int i = 1;i <= n;i++) {
int t1 = s[i][0] - 'a', t2 = s[i][s[i].length() - 1] - 'a';
if (!book[t1]) book[t1] = 1;
if (!book[t2]) book[t2] = 1;
//判断是否有欧拉回路的条件:无向图是否连通
//因此此处要构造无向图
add(t1, t2);
add(t2, t1);
// 一条从a到b的边(因为单词无法反着来)
in[t2]++; out[t1]++;
}
if (!judge()) {
cout << "***" << endl;
return 0;
}
int cnt = 0;
for (int i = 0;i < 26;i++) {
if (abs(in[i] - out[i]) >= 1)
{
cnt++;
if (abs(in[i] - out[i] > 1)) {
cout << "***" << endl;
return 0;
}
else if (out[i] - in[i] == 1 && head == -1)
head = i;
}
}
if (cnt && cnt != 2) {
cout << "***" << endl;
return 0;
}
else if (cnt == 2) dfs(head);
else
for (int i = 0;i < 26;i++) {
if (book[i])
dfs(i);
}
for (int i = ptr;i >= 1;i--) {
cout << ans[i];
if (i > 1) cout << ".";
}
return 0;
}
P1347 排序
(1)显然是TOPO排序问题,但是我们最后需要找的是一条链。
因此需要每个点都相互连通并且有一个顺序。
方法一:使用最长路(思路&code都来自洛谷的dalao)
建立超级源点与超级终点,与每一个字母连接,寻找最长路(最长路一定是包含每个字母的链),
如果这条链的dis[n+1] = n+2, (初始化dis[0]=1),说明是一条经过所有点的链;
如果dis[n+1]>n+2,说明内部有环;
如果dis[n+1]<n+2,说明条件不足。
#include"pch.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include<string.h>
#include <stack>
#include<cmath>
#include <iomanip>
#include<map>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 50;
int n, m, path[maxn];
vector<int> v[maxn];
bool g[maxn][maxn];
bool ff = 0;
void print(int v) {
if (path[v]) print(path[v]);
cout << char('A' + v - 1);
}
void bfs(int x) {
queue<int> q;
int dis[maxn] = { 0 };
dis[0] = 1;
q.push(0);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0;i < v[u].size();i++) {
int vv = v[u][i];
if (dis[vv] < dis[u] + 1) {
path[n + 1] = vv;
path[vv] = u;
dis[vv] = dis[u] + 1;
q.push(vv);
}
if (dis[vv] > n + 2) {
cout << "Inconsistency found after " << x << " relations.";
ff = 1;
exit(0);
}
}
}
if (x == m && dis[n + 1] != n + 2) {
cout << "Sorted sequence cannot be determined." << endl;
ff = 1;
exit(0);
}
else if (dis[n + 1] == n + 2) {
cout << "Sorted sequence determined after " << x << " relations: ";
print(path[n + 1]);
cout << ".";
ff = 1;
exit(0);
}
return;
}
int main() {
cin >> n >> m;
for (int i = 1;i <= n;i++) {
v[0].push_back(i);
v[i].push_back(n + 1);
}
for (int i = 1;i <= m;i++) {
char a, b, c;
cin >> a >> b >> c;
int aa = a - 'A' + 1, bb = c - 'A' + 1;
if(!g[aa][bb]){
v[aa].push_back(bb);
g[aa][bb] = 1;
}
bfs(i);
}
return 0;
}
方法二:TOPO排序
使用bfs进行拓扑排序,记录层数,同时减少入度,如果入度为零,把它放入队列进行下一层的探索。
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include<string.h>
#include <stack>
#include<cmath>
#include <iomanip>
#include<map>
#include<set>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 50;
int n, m;
struct Node {
int u, val;
Node(int u = 0, int val = 0) :u(u), val(val) {}
};
vector<int> ve[maxn];
int in[maxn],in2[maxn],sum, ans, k,cur=0;
set<int> s1;
void print() {
//再来一个topo
queue<int> q;
int in3[maxn];
memset(in3, 0, sizeof(in3));
for (int i = 0;i < 26;i++) {
for (int j = 0;j < ve[i].size();j++) {
int to = ve[i][j];
in3[to]++;
}
}
int head = -1;
for (int i = 0;i < 26;i++) {
// cout << char(i + 'A') << " : " << s1.count(i) << ", " << in3[i] << endl;
if (s1.count(i) && !in3[i]) {
head = i;
cout << char(i + 'A');
break;
}
}
q.push(head);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0;i < ve[u].size();i++) {
int to = ve[u][i];
in3[to]--;
if (in3[to] == 0) {
q.push(to);
cout << char(to + 'A');
}
}
}
cout << "." << endl;
}
void topo(int id) {
queue<Node> q;
int head = -1;
for (int i = 0;i < 26;i++) {
//出现过&入度为0的点是一层
if (in[i] == 0 && s1.count(i)) {
q.push(Node(i, 1));
sum++;
}
}
while (!q.empty()) {
int u = q.front().u;
int val = q.front().val;
q.pop();
for (int i = 0;i < ve[u].size();i++) {
int v = ve[u][i];
in[v]--;
if (in[v] == 0) {
sum++;
q.push(Node(v, val + 1));
//层数++;
ans = max(ans, val + 1);
}
}
}
if (ans == n) {
cout << "Sorted sequence determined after " << id << " relations: ";
print();
exit(0);
}
//如果还没有完成,也不应该出现sum!=cur的情况
if (sum != cur) {
cout << "Inconsistency found after "<<id<<" relations." << endl;
exit(0);
}
}
int main() {
cin >> n >> m;
for (int i = 1;i <= m;i++) {
string s;
cin >> s;
int a = s[0] - 'A', b = s[2] - 'A';
ve[a].push_back(b);
s1.insert(a);
s1.insert(b);
cur = s1.size();
in2[b]++;
sum = 0, ans = 0;
memcpy(in, in2, sizeof(in2));
topo(i);
}
cout << "Sorted sequence cannot be determined." << endl;
return 0;
}
其他
洛谷P1962
矩阵快速幂简单题
今天学习了矩阵快速幂,感觉主要还是推导
但是很难受,矩阵乘法写错了,debug好久啊
模板题 斐波那契使用矩阵快速幂
m = [1 1;1 0] f(n)=(1,1)*m^(n-2)
#include<iostream>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
const long long inf = 0x3f3f3f3f3f3f3f;
const int maxn = 6e3 + 5;
const int mod = 1e9 + 7;
const int Max_rank = 3;
struct Matrix {
long long a[Max_rank][Max_rank];
Matrix() {
memset(a, 0, sizeof(a));}
void ini() {
a[1][1] = a[1][2] = a[2][1] = 1;
a[2][2] = 0;
}
Matrix operator *(const Matrix x) {
Matrix res;
for (int i = 1;i <= 2;i++) {
for (int j = 1;j <= 2;j++) {
for (int k = 1;k <= 2;k++) {
res.a[i][j] = (res.a[i][j] + a[i][k] * x.a[k][j]) % mod;
}
}
}
return res;
}
};
ostream & operator << (ostream &out, Matrix &x) {
for (int i = 1;i <= 2;i++) {
for (int j = 1;j <= 2;j++) {
cout << x.a[i][j] << " ";
}
cout << endl;
}
return out;
}
long long ksm(long long n) {
Matrix ans, base;
ans.ini();base.ini();
//cout << ans << endl;
//cout << base << endl;
while (n >0) {
if (n & 1) { ans =base*ans; //cout << "???" << endl;
}
base = base * base;
//cout << base << endl;
//cout << ans << endl;
n >>= 1;
}
// cout << ans << endl;
return ans.a[1][1];
}
int main() {
//矩阵快速幂
//logN
long long n;
while (cin >> n) {
cout << ksm(n-2) << endl;
}
return 0;
}