【NOIP2017】SummerTraining0710
来源:互联网 发布:sql注入点 编辑:程序博客网 时间:2024/06/08 04:58
T1 并查集/二分图染色
T2 DP+矩阵乘法优化
T3 数位DP
100+100+30=230 rank13
想出了T2矩阵乘法好开森,然并卵……
%sqj wsj zhz大佬AK怒喷水题
后附zhz大佬的蔑视
看题解或代码的看目录,有索引。
T1
问题 A: 七天使的通讯
时间限制: 2 Sec 内存限制: 256 MB
题目描述
n个天使排成一条直线,某些天使之间需要互相联系,他们之间的通讯可以通过黑白两种通道中的一种;所有通道必须在直线同侧(另一侧是地面);为了保证通讯效率,同种颜色的所有通道之间不能相交。请计算能否建立这种通讯方案。
输入
第一行一个数T,表示接下来有T个询问。
对于每个询问:第一行两个数n,m,分别表示有n个天使、需要建立通讯线路的天使有m对;接下来有m行,每行两个数a、b,表示a、b两个天使需要通讯。
输出
对于每个询问,输出一行“sane”表示有可行方案、“non”表示无解
样例输入
1
7 5
1 3
2 7
3 4
7 4
6 5
样例输出
sane
提示
【样例解释】
样例中共有一个询问。
在(1,3)、(4,7)、(5,6)之间连黑色通道,在(2,7)、(3,4)之间连白色通道,每条通道都成功建立,且同种颜色的通道没有相交,所以输出sane。
【数据规模和约定】
对于 20%的数据,1<=n<=50,1<=m<=15
对于 50%的数据,1<=n<=1000,1<=m<=300
对于 100%的数据,1<=n<=5000,1<=m<=1000,1<=T<=10,1<=a<=n,1<=b<=n
数据保证每对(a,b)不重复,且a不等于b
【提示】
当两条线路有一对相同的端点时,这两条线路不相交。
也就是说,对于线路(a,b)和线路(c,d)(a
Solution
将每个通道设为一个节点,先暴力判断每两条通道如果是同种颜色会不会相交,如果会相交就在这两个节点之间连无向边,说明它们不能为同种颜色(必须在二分图两边)。然后对组成的无向图进行二分图判定(DFS染色),如果染色成功说明该图是一个二分图,即有解,否则无解。
PS:为什么在我眼中显然的并查集竟然是二分图……
Code
并查集
#include<bits/stdc++.h>using namespace std;const int MAXN=1010;int a[MAXN],b[MAXN];int fa[MAXN],val[MAXN],rank[MAXN];int n,m;inline bool pd(int a,int b,int c,int d){ return (a<c&&c<b&&b<d || c<a&&a<d&&d<b);}int getfa(int x){ if (fa[x]==x) return fa[x]; int y=fa[x]; fa[x]=getfa(fa[x]); val[x]=(val[x]+val[y])&1; return fa[x];}bool solve(){ for (int i=1;i<=m;i++) fa[i]=i,val[i]=0,rank[i]=0; for (int i=1;i<m;i++) for (int j=i+1;j<=m;j++) if (pd(a[i],b[i],a[j],b[j])) { int x=i,y=j; int fx=getfa(x),fy=getfa(y); if (fx!=fy) { if (rank[fx]<rank[fy]) { fa[fx]=fy; val[fx]=(val[y]+1-val[x])&1; } else { fa[fy]=fx; val[fy]=(val[x]+1-val[y])&1; rank[fx]++; } } else if (val[x]==val[y]) return false; } return true;}int main(){ int T; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d",&a[i],&b[i]); if (a[i]>b[i]) swap(a[i],b[i]); } if (solve()) printf("sane\n"); else printf("non\n"); } //system("pause"); return 0;}
二分图染色
#include<bits/stdc++.h>using namespace std;const int MAXN=1010;int a[MAXN],b[MAXN],color[MAXN];int Head[MAXN],Next[MAXN*MAXN],To[MAXN*MAXN];bool vis[MAXN];int n,m,tot;inline bool pd(int a,int b,int c,int d){ return (a<c&&c<b&&b<d || c<a&&a<d&&d<b);}void add(int x,int y){ tot++; Next[tot]=Head[x]; Head[x]=tot; To[tot]=y;}bool dfs(int u){ if (color[u]==-1) color[u]=1; vis[u]=true; for (int i=Head[u];i;i=Next[i]) { int v=To[i]; if (vis[v] && color[v]==color[u]) return false; if (!vis[v]) color[v]=1^color[u]; if (!vis[v] && !dfs(v)) return false; } return true;}int main(){ int T; scanf("%d",&T); while (T--) { tot=0; memset(Head,0,sizeof(Head)); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d",&a[i],&b[i]); if (a[i]>b[i]) swap(a[i],b[i]); } for (int i=1;i<=m;i++) for (int j=i+1;j<=m;j++) if (pd(a[i],b[i],a[j],b[j])) add(i,j),add(j,i); bool flag=false; memset(color,-1,sizeof(color)); memset(vis,false,sizeof(vis)); for (int i=1;i<=m;i++) if (!vis[i] && !dfs(i)) {printf("non\n"); flag=true; break;} if (!flag) printf("sane\n"); } return 0;}
T2
问题 B: 都市环游
时间限制: 1 Sec 内存限制: 512 MB
题目描述
因为SJY干的奇怪事情过多,SJY收到了休假的通知,于是他准备在都市间来回旅游。SJY有一辆车子,一开始行驶性能为0,每过1时间行驶性能就会提升1点。每个城市的道路都有性能要求。SJY一共有t时间休息,一开始他位于1号城市(保证1号城市道路要求为0),他希望在n号城市结束旅程。每次穿过一条城市间的路会花费1时间,当然他也可以停留在一个城市不动而花费1时间。当且仅当车子的行驶性能大于等于一个城市,我们才能到达那里。SJY希望知道,旅游的方案模10086后的答案。(只要在某一时刻通过的道路存在一条不相同,就算不同的方案)
输入
第一行三个数n,m,t,表示有n个城市m条道路t时间。
第二行n个数,hi表示第i个城市的道路性能要求。
第三到m+2行,每行两个数u,v,表示城市u与城市v之间有一条单向道路连接(可能有重边)。
输出
包括一个数字,表示旅游的方案模10086。
样例输入
5 17 7
0 2 4 5 3
1 2
2 1
1 3
3 1
1 4
4 1
4 5
5 4
5 3
4 1
2 1
5 3
2 1
2 1
1 2
2 1
1 3
样例输出
245
提示
【数据规模和约定】
对于20%的数据,n<=10,t<=80;对于50%的数据,n<=30,t<=80;对于100%的数据,n<=70,m<=1000,t<=100000000,hi<=70。
Solution
算法1:
暴力 ,搜索全部路径,发现每一步的行动可以用一个转移矩阵表示,每一步相当于乘上矩阵。
算法2:
由算法1得到,将每一步移动的转移矩阵都求出来,然后进行矩阵乘法,时间复杂度 O(n^3*t)
算法 3:
在算法2的基础上利用矩阵快速幂优化矩阵乘法,时间复杂度O(n^3*logt)
PS:为什么标解中每一步都是矩阵乘法啊,我觉得应该是DP,设f[i][j]表示到i城市j时间的方案数,在j>=t时f[i][j]+=f[k][j-1] 有一条k—>j的路径,然后才想到矩阵乘法优化的……莫非是我太菜了?
Code
#include<bits/stdc++.h> using namespace std; #define MOD 10086 const int MAXN=110; int f[MAXN][MAXN],a[MAXN][MAXN],h[MAXN]; int n,m,T,max_h; struct matrix { int a[MAXN][MAXN]; int row,col; matrix(int n=0) //Identity matrix; { row=col=n; memset(a,0,sizeof(a)); for (int i=1;i<=n;i++) a[i][i]=1; } friend matrix operator * (const matrix &a,const matrix &b) { if (a.col!=b.row) {cout<<"error!";} matrix c; c.row=a.row; c.col=b.col; for (int i=1;i<=a.row;i++) for (int j=1;j<=b.col;j++) { c.a[i][j]=0; for (int k=1;k<=a.col;k++) { c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%MOD; //if (c.a[i][j]>=MOD) c.a[i][j]%=MOD; } } return c; } }A,F; void pre() { scanf("%d%d%d",&n,&m,&T); for (int i=1;i<=n;i++) scanf("%d",&h[i]); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); a[x][y]++; } max_h=-1; for (int i=1;i<=n;i++) a[i][i]=1,max_h=max(max_h,h[i]); } matrix power(matrix a,int b) { matrix ans=matrix(n); while (b) { if (b&1) ans=ans*a; a=a*a; b>>=1; } return ans; } void solve() { f[1][0]=1; for (int j=1;j<=min(T,max_h);j++) { for (int i=1;i<=n;i++) { f[i][j]=0; if (j>=h[i]) for (int k=1;k<=n;k++) f[i][j]=(f[i][j]+f[k][j-1]*a[k][i])%MOD; } } if (T<=max_h) {printf("%d",f[n][T]); return;} // the rest use matrix mul; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) A.a[i][j]=a[i][j]; A.row=A.col=n; for (int i=1;i<=n;i++) F.a[1][i]=f[i][max_h]; F.row=1; F.col=n; A=power(A,T-max_h); F=F*A; cout<<F.a[1][n]; } int main() { pre(); solve(); //system("pause"); return 0; }
T3
问题 C: 大水题
时间限制: 1 Sec 内存限制: 512 MB
题目描述
dzy 定义一个n^2 位的数的生成矩阵A 为一个大小为n*n 且Aij 为这个数的第i*n+j-n位的矩阵。
现在dzy 有一个数n^2 位的数k,他想知道所有小于等于k 的数的n*n 生成矩阵有多少种。(如果不足n^2 位则补前缀零)
输入
第一行一个数n,第二行一个n^2 位的数k
输出
仅一行表示答案,答案可能很大,你只需输出答案对10^9 + 7 取模后的结果。
样例输入
2
1000
样例输出
954
提示
【数据规模和约定】
对于30% 的数据n<=2
对于100% 的数据n <=1000,且n为偶数
【提示】
如果两个生成矩阵在其中一个旋转180 度后可以重叠,则称这两个矩阵是相同的。
Solution
这题其实我也还没动,先上标称吧,
PS:zhz大佬的蔑视
Code
#include<iostream>#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>using namespace std;int x[1000010],mo=1000000007,n,f[1000010][2][2];char ch[1000011];int solve(){ memset(f,0x00,sizeof f); f[0][1][0]=1; for (int i=1;i<=n*n;i++){ for (int k=0;k<=9;k++) if (k<x[n*n-i+1]) f[i][0][0]=(f[i][0][0]+f[i-1][0][0]+f[i-1][0][1])%mo; else if (k>x[n*n-i+1]) f[i][0][1]=(f[i][0][1]+f[i-1][0][0]+f[i-1][0][1])%mo; else { f[i][0][0]=(f[i][0][0]+f[i-1][0][0])%mo; f[i][0][1]=(f[i][0][1]+f[i-1][0][1])%mo; } for (int k=0;k<x[i];k++) if (k<x[n*n-i+1]) f[i][0][0]=(f[i][0][0]+f[i-1][1][0]+f[i-1][1][1])%mo; else if (k>x[n*n-i+1]) f[i][0][1]=(f[i][0][1]+f[i-1][1][0]+f[i-1][1][1])%mo; else { f[i][0][0]=(f[i][0][0]+f[i-1][1][0])%mo; f[i][0][1]=(f[i][0][1]+f[i-1][1][1])%mo; } if (x[i]<x[n*n-i+1]) f[i][1][0]=(f[i-1][1][0]+f[i-1][1][1])%mo; else if (x[i]>x[n*n-i+1]) f[i][1][1]=(f[i-1][1][0]+f[i-1][1][1])%mo; else{ f[i][1][0]=f[i-1][1][0]; f[i][1][1]=f[i-1][1][1]; } } f[n*n][0][0]--; return (f[n*n][0][0]+f[n*n][1][0])%mo;}int solve2(){ int ans=0; for (int i=1;i<=n*n/2;i++) ans=(ans*10+x[i])%mo; for (int i=n*n/2+1;i<=n*n;i++) if (x[n*n-i+1]<x[i]) return ans; else if (x[n*n-i+1]>x[i]) return (ans-1+mo)%mo; return ans;}int main(){ freopen("water.in","r",stdin); freopen("water.out","w",stdout); scanf("%d",&n); scanf("%s",ch+1); for (int i=1;i<=n*n;i++) x[i]=ch[i]-'0'; int ans=0; for (int i=1;i<=n*n;i++) ans=(ans*10+ch[i]-'0')%mo; int k1=(1ll*(solve()-solve2()+mo)*500000004%mo+mo)%mo; ans=((ans-k1)%mo+mo)%mo; cout<<ans<<endl; return 0;}
- 【NOIP2017】SummerTraining0710
- noip2017
- NOIP2017
- noip2017
- NOIP2017
- NOIP2017
- NOIP2017
- NOIP2017
- NOIP2017
- 【NOIP2017】SummerTraining0720
- 【NOIP2017】SummerTraining0724
- 【NOIP2017】SummerTraining0726
- 【NOIP2017】SummerTraining0727
- 【NOIP2017】SummerTraining0721
- 【NOIP2017】SummerTraining0706
- 【NOIP2017】SummerTraining0707
- 【NOIP2017】Day1
- 【NOIP2017】Day2
- 23种设计模式汇总整理
- Flex 布局
- pdf转换cad如何进行转换才最简单呢?
- hive学习笔记
- 基于XML实现AOP
- 【NOIP2017】SummerTraining0710
- Bootstrap基础4——form表单
- intersection set
- Maven 简单使用创建Web项目
- pyhton爬虫(11)——抓取亚马逊商品类别信息
- 使用drawBitmap绘制图片
- 应届生落户上海申请及办理流程
- HDU1330 Deck
- RocketMQ简介