題意:
給你一串長度爲n的數字,對於裏面的每一個位置,第i個位置與第(i*i+1)%n個位置有一條單向的路徑
你可以從任意位置出發,走n次,得到n個字符組成的數字
讓你輸出你能得到的最大的數字
解析:
這道題做的時候真的想爆了..什麼反向建邊變成一棵樹,再用樹上倍增..還有tarjan縮點...什麼想法都想了..
但是後來看題解發現這是暴力直接做......要說線索可以打一下表,對於n<=150000,好像環的數量應該小於兩位數,
大部分是一位數。很多都是沒有入度的點.....
所以這裏暴力加剪枝就可以過。
其實觀察那種條件很苛刻的,你自己都舉不出很難的樣例來使複雜度增大的情況下,可以嘗試暴力+剪枝。
譬如說,這裏找最大的字符串,你很難舉出那種一直比到第n個位置才分出大小的字符串,就比兩個還比較好舉,但是當
需要比較的字符串一多,很多串大多都在中間或前面的位置被確定大小了。所以這種題目都可以嘗試暴力,只是需要合理剪枝。
先說題解的做法吧,bfs,對於同一層有兩個剪枝1.對於同一層,除了值最大的點可以向下找,其他都剪掉
2.對於同一層,如果在值最大的點中,存在相同的點,那麼把這些相同的點都刪掉只剩一個。即,上一層中
有很多最大值點,都指向同一個點。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <bitset>
using namespace std;
typedef long long ll;
const int MAX = 150000+100;
int n;
char s[MAX];
int mp[MAX];
int val[MAX];
char ans[MAX];
int Max;
int vis[MAX];
typedef struct node
{
int id;
int val;
int step;
int ord;
int nxtval;
node(int ii=-1,int vv=-1 ,int ss=-1,int oo=0,int nxt=0):id(ii),val(vv),step(ss),ord(oo),nxtval(nxt){}
friend bool operator <(node a,node b)
{
if(a.step==b.step) return a.nxtval<b.nxtval;
return a.step>b.step;
}
}node;
priority_queue<node> mq;
int vit[MAX];
void bfs()
{
ans[0]=Max+'0';
for(int i=1;i<n;i++)
ans[i]='0';
while(!mq.empty()){
node u=mq.top();
mq.pop();
if(!vis[u.ord]) continue;
if(u.step==n)
{
return;
}
node now;
now.id=mp[u.id];
now.step=u.step+1;
now.val=val[now.id];
now.ord=u.ord;
now.nxtval=val[mp[now.id]];
if(vit[now.id]==now.step) continue;
if(now.val+'0'>=ans[now.step])
{
mq.push(now);
ans[now.step]=now.val+'0';
vit[now.id]=now.step;
}
else
{
vis[u.ord]=0;
}
}
}
int main()
{
int T;
int cnt=0;
scanf("%d",&T);
int cas=0;
while(T--)
{
memset(vis,0,sizeof(vis));
memset(vit,0,sizeof(vit));
cas++;
cnt=0;
scanf("%d",&n);
//getchar();
scanf("%s",s);
while (!mq.empty())
{
mq.pop();
}
Max=0;
for(int i=0;i<n;i++){
mp[i]=(1ll*i*i+1)%n;
val[i]=s[i]-'0';
Max=max(Max,val[i]);
}
for(int i=0;i<n;i++){
if(val[i]==Max){
mq.push(node(i,val[i],0,cnt,val[mp[i]]));
vis[cnt++]=1;
}
}
//if(cnt<n){
//}
printf("Case #%d: ",cas);
bfs();
for(int i=0;i<n;i++)
printf("%c",ans[i]);
printf("\n");
}
}
其實關鍵就是第二個剪枝,當深度爲x時,走到了w點,但是前面已經存在當深度爲x,走到w點的狀態,那麼接下來這兩種狀態
都是一樣的,所以可以剪掉。所以我把之前寫的迭代版,加了has,來存這個狀態。也過了,雖然用2.5s,因爲map二分有常數
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <bitset>
using namespace std;
typedef long long ll;
const int MAX = 150000+100;
int n;
char s[MAX];
int mp[MAX];
int val[MAX];
int hd[MAX];
int cas;
map<int,int> has[MAX];
bool judge(int now,int ind)
{
int flag=0;
int cnt=0;
int h1=0,h2=0;
int tim=0;
while (tim<n)
{
tim++;
if(has[ind].count(tim))
{
flag=0;
break;
}
has[ind][tim]=1;
if(now==ind)
{
flag=0;
break;
}
if(s[now]<s[ind])
{
flag=1;
break;
}
else if(s[now]>s[ind])
{
flag=0;
break;
}
now=mp[now];
ind=mp[ind];
}
if(!flag) return 0;
else return 1;
}
int main()
{
int T;
int cnt=0;
scanf("%d",&T);
cas=0;
while(T--)
{
cas++;
cnt=0;
scanf("%d",&n);
//getchar();
scanf("%s",s);
int Max=0;
for(int i=0;i<n;i++){
mp[i]=(1ll*i*i+1)%n;
val[i]=s[i]-'0';
Max=max(Max,val[i]);
has[i].clear();
}
for(int i=0;i<n;i++){
if(val[i]==Max){
hd[cnt++]=i;
}
}
int now=hd[0];
if(cnt<n){
for(int i=1;i<cnt;i++){
if(judge(now,hd[i]))
now=hd[i];
}
}
printf("Case #%d: ",cas);
for(int i=0;i<n;i++)
{
printf("%c",s[now]);
now=mp[now];
}
printf("\n");
}
}
做法二:就是用倍增的思想,nxt[i][j]表示i走2^j步到達的點
st[i][j]表示[i,i走2^j到達的點)中間的路徑的字符串的hash值
然後因爲字符串hash只能判斷兩個字符串是否相等,不能判斷大小
所以我們通過每一層比較st[x][len]與st[x][len]是否相等,
不相等的話,如果前一部分有不相等,則遞歸到前一部分
否則到後一部分。
一直到len=0時,直接比較兩個字符s[x]<s[y]
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 150000+100;
const int MOD = 1e9+7;
char s[N];
ll base=10;
ll p[N*2];
int nxt[N][20];
ll st[N][20];
int max0=18;
int bit[31];
int mp[N];
void init_hash()
{
p[0] = 1;
for(int i = 1; i <N+N; i ++)p[i] =p[i-1] * base%MOD;
bit[0]=1;
for(int i=1;i<=max0;i++) bit[i]=bit[i-1] * 2;
//1235->1,12,123,1235
}
void init_st(int n)
{
memset(nxt,0,sizeof(nxt));
memset(st,0,sizeof(st));
for(int i=1;i<=n;i++)
nxt[i][0]=mp[i];
for(int j=1;j<=max0;j++) //[i,i的第2^j次方個父親)
for(int i=1;i<=n;i++)
nxt[i][j]=nxt[nxt[i][j-1]][j-1];
for(int i=1;i<=n;i++)
st[i][0]=s[i]-'0';
for(int j=1;j<=max0;j++)
for(int i=1;i<=n;i++)
st[i][j]=(st[i][j-1]*p[bit[j-1]]%MOD+st[nxt[i][j-1]][j-1])%MOD;
}
/*ull get(int l, int r, ull g[]){//取出g裏l - r裏面的字符串的hash值
return g[r] - g[l - 1] * p[r - l + 1];
}*/
int judge(int x,int y,int len)
{
if(len==0)
{
return s[x]<s[y];
}
if(st[x][len]==st[y][len])
return 0;
else if(st[x][len-1]!=st[y][len-1])
{
return judge(x,y,len-1);
}
else
{
return judge(nxt[x][len-1],nxt[y][len-1],len-1);
}
}
int main()
{
int t;
scanf("%d",&t);
init_hash();
int cas=0;
while(t--)
{
int n;
scanf("%d",&n);
scanf("%s",s+1);
for(ll i=0;i<n;i++)
{
mp[i+1]=(i*i+1)%n+1;
}
init_st(n);
int now=1;
for(int i=2;i<=n;i++)
{
if(judge(now,i,max0))
now=i;
}
cas++;
printf("Case #%d: ",cas);
for(int i=1;i<=n;i++)
{
printf("%c",s[now]);
now=mp[now];
}
printf("\n");
//for(int i = 0; i < n; i ++)
// has[i] = has[i - 1] * base + (s[i] - '0');
}
}