洛谷快乐学习

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值