这场比赛赛后反应坑点很多,比如数据太弱,比如各种坑。。。。然而对自己的练习价值还是很大的
首先上个链接:2015四川省赛4436-4445
百度到的题解:点我看百度题解
先分析每个题,再来分析比赛
A:水题:每个开根号判断就好了
int main(){ //input; while(scanf("%d",&n)!=EOF){ int flag=1; for(int i=1;i<=n;i++){ scanf("%d",&num); if (int(sqrt(num*1.0)*sqrt(num*1.0))!=num) flag=0; } if (flag) puts("Yes"); else puts("No"); } return 0; }
脑洞数学题,拿个数举例子的话,68823:
要找尾数3的进位,要找其他数的个位大于等于7的
要找尾数23的进位,要找其他数的个位大于等于77的
要找尾数823的进位,要找其他数的个位大于等于177的
要找尾数8823的进位,要找其他数的个位大于等于1177的
要找尾数68823的进位,要找其他数的个位大于等于31177的
现在的思路已经有了,把每个数的尾数全部截出来,然后跟其他的数的同样长度的尾数进行比较,看看能不能进位。。
但是这样的时间是O(n^2)的
但是,我们用最简单的办法减小复杂度!~二分!
所以,最终的方法为,每个数截取同样长度的数位,各个数位单独从小到大查找自己所需要的“补数”,如823找大于等于177的(但是有个细节,找的时候可能会找到“自己”,待会处理),然后累加即可
上代码:(处理点为 ans - - 的代码)
vector<ll> G[20]; int x[maxn]; inline void in(int &x){ char ch; x=0; ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } } int main(){ int n; while(scanf("%d",&n)!=EOF){ for(int i=1;i<=9;i++) G[i].clear(); for(int i=1;i<=n;i++){ in(x[i]); ll tp=10; for(int k=1;k<=9;k++){ G[k].push_back(x[i]%tp); tp*=10; } } for(int i=1;i<=9;i++) sort(G[i].begin(),G[i].end()); ll ans=0; for(int i=1;i<=n;i++){ ll tp=10; for(int k=1;k<=9;k++){ ll tt=x[i]%tp; tt=tp-tt; int u=lower_bound(G[k].begin(),G[k].end(),tt)-G[k].begin(); ans+=G[k].size()-u; if (tt<=x[i]%tp) ans--; tp*=10; } } printf("%lld\n",ans/2); } return 0; }
C题:字符串,给定A和B串,要求在B串中找到A串第一次出现的位置然后删除,将其未删除的左右两部分合并之后继续操作,问最后还剩下哪些字符
这个算法的精髓就是Next数组,因为在暴力操作中一旦匹配失败是退回到初始位置继续,而Next数组的处理之后能够退回到“”最有可能"再次匹配的地方(利用之前所有的匹配信息),弄懂了这一点,这个题就好做
然后,很明显,需要kmp_pre处理A串,然后把B串中的匹配字符串删除,未匹配的存在一个新的字符串中,仍然维护B在A中的Next数组
代码如下(特别简洁):其中pos数组仍然代表失去匹配之后下个位置去哪儿找
void kmp_pre(char x[],int m,int Next[]){ int i=0,j; j=Next[0]=-1; while(i<m){ while(j!=-1&&x[i]!=x[j]) j=Next[j]; Next[++i]=++j; } } int Next[N],pos[N]; char stk[N]; int KMP_Count(char x[],int m,char y[],int n){ int i,j,ip; kmp_pre(x,m,Next); i=j=ip=0; while(ip<n){ stk[i]=y[ip++]; while(j!=-1&&stk[i]!=x[j]) j=Next[j]; i++;j++; pos[i]=j; if (j>=m){ i-=m; j=pos[i]; } } return i; } char pat[N],src[N]; int main(){ while(scanf("%s%s",pat,src)==2){ int l=KMP_Count(pat,strlen(pat),src,strlen(src)); stk[l]='\0'; puts(stk); } return 0; }
弱用二分图匹配水过,,,然而并不能正确的,但是弱弱在这贴个板子
int uN,vN; int g[maxn][maxn]; int linker[maxn]; bool used[maxn]; bool dfs(int u){ for(int v=1;v<=vN;v++) if (g[u][v]&&!used[v]){ used[v]=true; if (linker[v]==-1||dfs(linker[v])){ linker[v]=u; return true; } } return false; } int hungary(){ int res=0; memset(linker,-1,sizeof(linker)); for(int u=1;u<=uN;u++){ memset(used,false,sizeof(used)); if (dfs(u)) res++; } return res; } int main(){ //input; int n,m,u,v; while(scanf("%d%d",&n,&m)!=EOF){ memset(g,0,sizeof(g)); while(m--){ scanf("%d%d",&u,&v); g[u][v]=g[v][u]=1; } uN=vN=n; printf("%d\n",hungary()/2); } return 0; }
vector<int> adjs[MAXN]; int last[510]; bool mat[MAXN][MAXN], vis[MAXN]; int n, m; int res; bool mustselect(int u) { for(int i = 0; i < u; ++i) if(!vis[i] && mat[i][u]) return true; return false; } void dfs(int u, int c) { if(c >= res) return ; if(u == n) { res = c; } else { vis[u] = true; dfs(u + 1, c + 1); vis[u] = false; if(!mustselect(u)) { int t = 0; foreach(it, adjs[u]) if(last[*it] == -1) { last[*it] = u; t++; } dfs(u + 1, c + t); foreach(it, adjs[u]) if(last[*it] == u) last[*it] = -1; } } } int solve() { memset(vis, 0, sizeof(vis)); res = n; dfs(0, 0); return res; } int main() { while(scanf("%d%d", &n, &m) != EOF) { memset(mat, 0, sizeof(mat)); memset(last, -1, sizeof(last)); for(int i = 0; i < 30; ++i) adjs[i].clear(); for(int i = 0, a, b; i < m; ++i) { scanf("%d%d", &a, &b); a--, b--; if(a > b) swap(a, b); if(b < 30) mat[a][b] = mat[b][a] = true; else adjs[a].push_back(b); } n = min(n, 30); printf("%d\n", solve()); } return 0; }
E题:完美数学题
首先可知,一个任意的长为x宽为y的矩形,如果可以放入n*m的矩形中,那么方案数为(n-x+1)(m-y+1)
现在开始分析:由于对称性,简化细节,保证n大于等于m(如果n<m的话可以swap)
那么,对x和y的限制有三条,x小于等于n,y小于等于m,2(x+y)小于等于k,可知取min()
由于n和m的范围限制,我们可以枚举一维再做考虑,不妨枚举x
则有x的范围为1到min(n,k/2)
那么y的范围是1到min(k/2-x,m)
循环中不能对y循环,但是,发现!!y是从1开始连续的,把公式拆成(n-x+1)(m+1)-y(n-x+1)
y的变化是知道的!所以只需枚举x即可,y得到最大值就可以算出上述的值
int main(){ //input; while(scanf("%lld%lld%lld",&n,&m,&k)!=EOF){ ans=0; if (n<m) swap(n,m); for(w=1;w<=min(k/2-1,n);w++){ l=min(k/2-w,m); r=(l+1)*l/2; ans+=l*(n-w+1)*(m+1)-(n-w+1)*r; } cout<<ans<<endl; } return 0; }
思路:枚举10000这个数据点,然后用树状数组维护终点为i的最大的和(左边求一个,右边求一个),再枚举切断点取最大值即可
int lowbit(int x){ return x&(-x); } void modify(int x,int val){ while(x){ tree[x]=max(tree[x],val); x-=lowbit(x); } } int query(int x){ int res=0; while(x<=m){ res=max(res,tree[x]); x+=lowbit(x); } return res; } void init(int n){ memset(tree,0,sizeof(tree)); lmax[0]=0; for(int i=1;i<=n;i++){ if (a[i]==0||a[i]==m) lmax[i]=lmax[i-1]; else{ lmax[i]=a[i]+query(a[i]); modify(a[i],lmax[i]); lmax[i]=max(lmax[i],lmax[i-1]); } } memset(tree,0,sizeof(tree)); rmax[n+1]=0; for(int i=n;i>0;i--){ if (a[i]==0||a[i]==m) rmax[i]=rmax[i+1]; else{ rmax[i]=a[i]+query(a[i]); modify(a[i],rmax[i]); rmax[i]=max(rmax[i],rmax[i+1]); } } } int solve(){ int res=0; for(int i=0;i<n;i++) if (b[i]==m){ for(int j=1;j<n;j++) a[j]=b[(i+j)%n]; init(n-1); res=max(res,max(lmax[1],rmax[n-1])); for(int i=1;i<n-1;i++) res=max(res,lmax[i]+rmax[i+1]); } return res+m; } int main(){ //input; while(scanf("%d",&n)!=EOF){ for(int i=0;i<n;i++) scanf("%d",&b[i]); printf("%d\n",solve()); } return 0; }
G和H弱弱不会。。。。。。。。。。
I题:坑广搜(暴力还是保平安啊。。。。。)
题意:给定n点完全图,其中m条边长度为a,剩下的所有边长度都为b,求1到n的最短路径
这个题的最大难度不是思路,而是数据量:n最大100000,所以存数据不能用矩阵,跑算法不能O(n^2)
弱比赛的时候没过,赛后巨巨们的思路是用链表或者并查集之类的数据结构维护:当前那些点没有访问过
代码如下:
int head[MAXV], ecnt; int to[MAXE], nxt[MAXE]; int n, m, a, b; void init() { memset(head + 1, -1, n * sizeof(int)); ecnt = 0; } void add_edge(int u, int v) { to[ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt++; to[ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt++; } int dis[MAXV]; int solvea() { fill(dis + 1, dis + n + 1, b); dis[1] = 0; queue<int> que; que.push(1); while(!que.empty()) { int u = que.front(); que.pop(); for(int p = head[u]; ~p; p = nxt[p]) { int v = to[p]; if(dis[u] + a < dis[v]) { dis[v] = dis[u] + a; que.push(v); } } } return dis[n]; } int cnt[MAXV]; int solveb() { fill(dis + 1, dis + n + 1, a); dis[1] = 0; priority_queue<pair<int, int> > que; memset(cnt + 1, 0, n * sizeof(int)); for(int p = head[1]; ~p; p = nxt[p]) cnt[to[p]]++; for(int i = 2; i <= n; ++i) que.push(make_pair(-cnt[i], i)); int viscnt = 1, d = b; while(!que.empty() && d < a) { int upper = viscnt; while(!que.empty() && -que.top().first < upper) { int c = -que.top().first, u = que.top().second; que.pop(); if(cnt[u] != c) continue; viscnt++; dis[u] = d; for(int p = head[u]; ~p; p = nxt[p]) { int v = to[p]; if(dis[v] == a && cnt[v] < upper) { cnt[v]++; que.push(make_pair(-cnt[v], v)); } } } d += b; if(viscnt == upper) break; } return dis[n]; } int main() { while(scanf("%d%d%d%d", &n, &m, &a, &b) != EOF) { init(); for(int i = 0, u, v; i < m; ++i) { scanf("%d%d", &u, &v); add_edge(u, v); } if(a < b) printf("%d\n", solvea()); if(a == b) printf("%d\n", a); if(a > b) printf("%d\n", solveb()); } }(虽然比赛的时候各种水的姿势都能过。。。然而,赛后学到方法为好)
J题:暴力枚举题
从(0,0)开始,根据题意,暴力找石头看看会不会撞到,如果会,找一个最近的提前转弯
注意细节点:(1)最大值不够大,在比较点的时候会WA(2)4种方向的最大最小不同,而且求最近
struct NODE { int x,y; int tow; int vis[4]; }node[2000]; int main(){ //input; int i,j,k,l,t,n,ans; while(scanf("%d",&n)!=EOF){ memset(node,0,sizeof(node)); for(i=1;i<=n;i++) scanf("%d%d",&node[i].x,&node[i].y); NODE now; now.tow=0; now.x=now.y=0; ans=0; while(1){ t=0; int mini=INF,maxi=-INF; if(now.tow==0){ for(i=1;i<=n;i++){ if(node[i].y==now.y&&node[i].x>now.x&&mini>node[i].x){ mini=node[i].x; t=i; } } if(t!=0) now.x=node[t].x-1; } if(now.tow==1){ for(i=1;i<=n;i++){ if(node[i].x==now.x&&node[i].y<now.y&&maxi<node[i].y){ maxi=node[i].y; t=i; } } if(t!=0) now.y=node[t].y+1; } if(now.tow==2){ for(i=1;i<=n;i++){ if(node[i].y==now.y&&node[i].x<now.x&&maxi<node[i].x){ maxi=node[i].x; t=i; } } if(t!=0) now.x=node[t].x+1; } if(now.tow==3){ for(i=1;i<=n;i++){ if(node[i].x==now.x&&node[i].y>now.y&&mini>node[i].y){ mini=node[i].y; t=i; } } if(t!=0) now.y=node[t].y-1; } if(t==0){ cout<<ans<<endl; break; } else if(node[t].vis[now.tow]==1){ cout<<-1<<endl; break; } else{ ans++; node[t].vis[now.tow]=1; now.tow=(now.tow+1)%4; } } } return 0; }
————————————————————————————————————————————————————————
今天,中国篮球又让我找到了自己的兴奋点!明天加油!