今天是我们组出题,也做了一下。过了4题,感觉题目难度不大。多想想欧拉路那道题说不定也可以做。还顺带过了不是今天比赛的一道线段树合并。感觉代码能力的确有所提高(算是恢复)。但是离应有的状态还是差很远!
今天的几何题很简单但是没有去写,因为几何的板子太陌生。其实几何的东西不多,只要熟悉几个基本的模型,就比较好做。一定不要期望ACM队里有人帮你写几何题,因此这个假期一定要练好几何!每天至少1道。先把板子敲熟再应用。做到轻松过题。
今天下午和晚上的状态很差,写题效率极低,必须反思!坚持到最后一刻!不能放松!适当的运动后应该静下心来写题!控制住自己!高效的写题!
今天我出的题(虽然不是原创)感觉还是不错的。然而没有人来写,有点小遗憾!
这两天的题还有4道没有调你在干什么,明天必须一起全部调完!!!
题解:
A:推结论。然后暴力。挺显然的,就是思维要放开
B:几何。圆与矩形求交转化成把矩形扩展(四角变成1/4圆)和圆心运动的直线求交。
今天没有写完(打球去了,忘了自己应该做的事!)明天写
C:签到题。WA了两发因为没有看清楚题意。题目上说的是prime number
D:每个位置有pi概率为1,求区间连续1的长度3次方的期望。单点修改。
把dp贡献差分,维护1,2,3次,矩阵快速幂维护转移。利用矩阵的结合律(直接DP无法结合)
经典题
//把f数组维护成贡献增加(前缀和),矩阵快速幂的时候就不用记ans了,优化一维
//线段树维护矩阵,动态DP,经典思想。因为矩阵满足结合律,而直接dp无法统计左边对右边的贡献
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 200020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
typedef long long ll;
inline int read(){
register int num = 0;
register char ch = getchar();
while ( ch > '9' || ch < '0' ) ch = getchar();
while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
return num;
}
int num[10];
inline void write(int x){
register int cnt = 0;
if ( !x ){ printf("0"); return; } //一定要特判0!!!
while ( x ) num[++cnt] = x % 10 , x /= 10;
while ( cnt ) putchar(num[cnt--] + '0');
}
double a[4][4],b[4][4],tmp[4][4],p[maxn],mtx[maxn][4][4];
int ls[maxn],rs[maxn],tot,root;
int n,m,T;
void clear(){
rep(i,0,tot) memset(mtx[i],0,sizeof(mtx[i])) , ls[i] = rs[i] = 0;
tot = root = 0;
}
inline void power(double a[4][4],double b[4][4],double c[4][4]){
memset(tmp,0,sizeof(tmp));
//rep(i,0,4) rep(j,0,4) tmp[i][j] = 0;
rep(i,0,3) rep(j,0,3) rep(k,0,3) tmp[i][j] += b[i][k] * c[k][j];
memcpy(a,tmp,sizeof(tmp));
}
void update(int x){
power(mtx[x],mtx[ls[x]],mtx[rs[x]]);
}
void build(int &x,int l,int r){
x = ++tot;
if ( l == r ){
mtx[x][1][1] = mtx[x][2][2] = mtx[x][0][3] = mtx[x][1][3] = mtx[x][2][3] = p[l];
mtx[x][0][1] = mtx[x][0][2] = p[l] * 3 , mtx[x][1][2] = p[l] * 2;
mtx[x][0][0] = mtx[x][3][3] = 1;
return;
}
int mid = (l + r) >> 1;
build(ls[x],l,mid) , build(rs[x],mid + 1,r);
update(x);
}
void modify(int x,int l,int r,int id,double cp){
if ( l == r ){
p[l] = cp;
mtx[x][1][1] = mtx[x][2][2] = mtx[x][0][3] = mtx[x][1][3] = mtx[x][2][3] = p[l];
mtx[x][0][1] = mtx[x][0][2] = p[l] * 3 , mtx[x][1][2] = p[l] * 2;
mtx[x][0][0] = mtx[x][3][3] = 1;
return;
}
int mid = (l + r) >> 1;
if ( id <= mid ) modify(ls[x],l,mid,id,cp);
else modify(rs[x],mid + 1,r,id,cp);
update(x);
}
void query(int x,int l,int r,int L,int R,double a[4][4]){
if ( L <= l && R >= r ) { power(a,a,mtx[x]); return; }
int mid = (l + r) >> 1;
if ( L <= mid ) query(ls[x],l,mid,L,R,a);
if ( R > mid ) query(rs[x],mid + 1,r,L,R,a);
}
int main(){
// freopen("input.txt","r",stdin);
scanf("%d",&T);
while ( T-- ){
clear();
scanf("%d",&n);
rep(i,1,n) scanf("%lf",&p[i]);
build(root,1,n);
scanf("%d",&m);
while ( m-- ){
int tp,x,l,r; double cp;
scanf("%d",&tp);
if ( tp == 1 ){
scanf("%d %lf",&x,&cp);
modify(root,1,n,x,cp);
}
else{
scanf("%d %d",&l,&r);
memset(a,0,sizeof(a));
rep(i,0,3) a[i][i] = 1;
query(root,1,n,l,r,a);
double ans = a[0][3];
printf("%.2f\n",ans);
}
}
}
return 0;
}
E:一道欧拉回路的好题。[1,m]的数各出现n次,每次与0交换(开始0在末尾)使[k * m + 1,k *m + m]的区间中[1,m]恰好各出现一次。
对于每一个区间,如果一个值多了则向该数连边,少了则该数向它连边。对于每个联通块找欧拉回路。因为出入度相等所以一定存在。
输出方案在欧拉回路退栈的时候推下细节。非常好写。但是因为看错数据范围调了很久!很不应该!
求欧拉回路要边要dfs完后加入栈,保证边的顺序
//看清数据范围再写题!!!
#include<bits/stdc++.h>
using namespace std;
#define maxn 820
#define N 200020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
struct node{
int next,to,from;
}e[N];
struct node2{
int x,y;
};
int head[maxn],cnt;
int n,m,T,stack_[N],tops,tag[N];
int a[maxn][maxn],num[maxn][maxn],tmp,vis[maxn];
vector <int> vec[maxn][maxn];
vector <node2> ans;
void clear(){
rep(i,1,n + m) vis[i] = head[i] = 0;
rep(i,1,n) rep(j,1,m) num[i][j] = 0 , vec[i][j].clear();
rep(i,1,cnt) tag[i] = 0;
ans.clear(); cnt = 0;
}
inline void adde(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].from = x;
head[x] = cnt;
}
void dfs(int x){
vis[x] = 1;
for (int i = head[x] ; i ; i = e[i].next){
if ( tag[i] ) continue;
tag[i] = 1;
dfs(e[i].to);
stack_[++tops] = i; //求欧拉回路要后加入栈,保证边的顺序
}
}
int main(){
freopen("input.txt","r",stdin);
freopen("1.out","w",stdout);
while ( ~scanf("%d %d",&n,&m) ){
tmp = n * m + 1;
rep(i,1,n){
rep(j,1,m) scanf("%d",&a[i][j]) , vec[i][a[i][j]].push_back((i - 1) * m + j) , num[i][a[i][j]]++;
rep(j,1,m){
if ( !num[i][j] ) adde(j,m + i);
else rep(k,2,num[i][j]) adde(m + i,j);
}
}
rep(i,m + 1,n + m){
if ( !vis[i] ){
int fir = head[i],id,cur,last,num;
tops = 0 , dfs(i);
if ( !fir ) continue;
last = vec[i - m][e[fir].to].back();
vec[i - m][e[fir].to].pop_back();
ans.push_back((node2){last,tmp});
for(int j = 1 ; j <= tops ;j++){
id = stack_[j], num = e[id].from;
id = stack_[++j];
if ( j == tops ) cur = n * m + 1;
else{
cur = vec[e[id].from - m][num].back();
vec[e[id].from - m][num].pop_back();
}
ans.push_back((node2){last,cur});
last = cur;
}
}
}
printf("%d\n",ans.size());
for (int i = 0 ; i < ans.size() ; i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
clear();
}
return 0;
}
F:求最少删多少个数是最长上升子序列变短。
按f[i](dp值)分层建图。最小割。
好久没写网络流的题。考场上现场yy出来的。
但是应该加强网络流的练习,别把强项丢了
G:我自己的题。见我的出题日志。
最后,提醒几点:
1。早点睡,保证考试时精力充沛!
2。一定要复习,有10多天没有复习以前的题了
3。坚持到最后一刻,一直到开学都不能有一点松懈。把自己的弱项静心补起来,相信自己OI的实力!
4。明天打好翻盘仗!静下心来思考!争取过5题!进入前5名!